Common Lisp Object System
（コモン リスプ オブジェクトシステム、略称 CLOS）は、ANSI Common Lisp (CL) の一部をなすオブジェクト指向プログラミング機能であり、他の類似の言語（EuLisp や Emacs Lisp）にも導入されている。当初アドオンとして提案され、ANSIの標準に組み込まれた。CLOS は"強い型付け"をもつ(無名クラスは許されない)動的(実行時に定義を変更できる)オブジェクトシステムであり、C++やJavaのような静的なオブジェクト指向言語とは大きく異なる。初期のLISPオブジェクトシステム（MIT Flavors や Common LOOPS）に影響されているが、より汎用的である。
LISPにオブジェクト指向を導入することは簡単である。2ページ程度のコードがあれば実現できる（Graham, 1994）。一方、オブジェクト指向LISPを柔軟で拡張性に富んだものに設計するのはより困難であった。CLOS は完全なオブジェクトシステムであり、オブジェクト指向風に実装されている。CLOS のオブジェクト指向実装は CLOS Metaobject Protocol (MOP) と呼ばれ、これによってカスタマイズや拡張が可能となっている。
CLOS は多重ディスパッチシステムである。すなわち、引数のデータ型によってメソッドを用意できる。多くのオブジェクト指向言語は単一ディスパッチであり、メソッドは第一引数のデータ型でしか多重化できない。CLOS のメソッドは総称関数にグループ化される。総称関数は同じ名前と引数構造を持つ（ただし個々の引数のデータ型が異なる）メソッドを集めたものである。後の例によりこのことをより良く説明する。
多くの動的オブジェクト指向言語（Pythonなど）と同様、CLOS ではカプセル化が行われない。任意のデータ（スロット）にcodice_1 関数でアクセス可能である。
データ構造や関数管理にあたっては、CL のプログラマはパッケージ機能を用いる。その意味で、カプセル化はファイル単位やクラス単位ではなくパッケージ単位で行われる。具体的には、スロットの名前を外部に公開するか否かを選ぶことによって未知のプログラムからオブジェクトの中身を保護する。
CLOS は多重継承を許している。菱形継承問題は、メソッド結合度の戦略によって異なる解決法を選ぶことができる。デフォルトのメソッド結合(メソッド・コンビネーション)は、菱形継承問題を左から右の規則で処理する。
CLOSでのオブジェクトのクラスは動的であり、オブジェクトの内容だけでなく「構造」を実行時に変更できる。インスタンスが属するクラスを変更する関数は codice_2 である。
また、CLOS は実行時に（既にそのクラスがインスタンスを持っていても）クラス定義を変更できる。具体的には、codice_3を再度評価してクラス定義を変更した際、CLOSはcodice_4を呼び出す。codice_5は指定されたクラスのすべてのインスタンスに対してcodice_6を呼び出す。codice_6は新たに追加されたスロットや削除されたスロットの情報を引数として受けとり、同じく引数として受け取ったインスタンスから新しい定義に基づいて新たに作ったインスタンスへ値をコピーする。これら３つのメソッドは継承によりユーザーが書き換え・追加を行うことができる。
CLOS はプロトタイプベースではない。インスタンスをあるクラスのメンバーとして作成するには事前にcodice_3によってそのクラスを定義しなければならない。
ANSI 標準の範囲外だが、CLOS の実装に広く採用されている拡張としてメタオブジェクトプロトコル(The Common Lisp Object System MetaObject Protocol)がある。MOP は CLOS 実装基盤に標準インタフェースを定義し、クラスをメタクラスのインスタンスとして扱い、新たなメタクラスを定義したり、基底クラスの振る舞いを修正したりできる。CLOS MOP はアスペクト指向プログラミングの先取りとも言え、実際同じ技術者（Gregor Kiczales など）が関わっている。代表的な機能はcodice_9、codice_10、など。
MOPは実装系によって扱いが異なるが、その重要性のため、可搬性を担保する試みが行われている。結果、現在では、インターフェースを共通にするライブラリ Closer to MOP がデファクトスタンダードとなっている。
総称関数を呼び出した時、実行する手続きの内容は、その実行時に動的に決定される。コンパイラによる最適化がある場合もある。codice_11宣言によってタイプをコンパイラに指定しない場合、すべての型判定とメソッドの形成が実行時に行われるため、速度は非常に遅くなる。
引数の型と継承関係に応じて、適用してよいメソッドを集める。
クラスcodice_12がクラスcodice_13を継承しているとする。p1とp2は共にcodice_12のインスタンスである。総称関数codice_15の以下のような呼び出し
について、メソッドが以下のように定義されている場合、
上の4つはすべて適用可能メソッドである。
これら4つのメソッドは、引数オブジェクトの継承順序(Precedence Order)に従ってソートされる。
上の例では、呼び出す際のp1とp2が共にクラスpersonのインスタンスであるため、それに最もマッチしている4番目のメソッドが最も高い継承順序を持つ。
継承順序は通常、左側の引数から順に計算される。従って上の例では、2番目と3番目のメソッドを比べた場合、第一引数の適用度が優先されることから、2番目の適用度のほうが高くなる。
結果的に、メソッドらは4,2,3,1の順でソートされる。
CLOSでは、上のソートによって作られたメソッドのリストに一定の戦略を適用することで、最終的に実際に行う動作を決定する。この戦略のことをメソッド結合と呼ぶ。いくつかのバリエーションが標準で定義されている。
他の言語において、あるインスタンスのメソッドを呼び出すときの動作について考えてみよう。そのインスタンスのクラスが親クラスを持つとき、例えばJavaのような言語においては、継承されたメソッドは上書きされてしまう。一方,CLOSではそのような通常の上書き(オーバーライド)戦略だけにとどまらず、多種多様な戦略がANSI標準で定義され、かつ自由に定義できる。
メソッド結合法則は総称関数の定義ごとに指定する。標準メソッド結合以外のメソッド結合法則を指定した場合、メソッド定義の際にはメソッド指定子(Qualifier)を指定して、結合法則に固有の機能を使うことを指定しなくてはいけない。それぞれのメソッド結合は複数のメソッド指定子を持つ。なお、すべてのメソッド結合はcodice_17メソッド結合を持たなくてはならないと定義されている。
これはjavaのもつ継承戦略と共通点がある。標準メソッド結合では、指定子を指定しない( "unspecified method qualifier" )メソッドのことをプライマリ・メソッド ( "Primary method" )と呼び、これには上書き戦略を用いる。また、その他に:around,:before,:afterメソッド結合を持つ。
メソッド定義の中では、(call-next-method)という特殊な関数を呼ぶことができ、これは次に適用すべきメソッドを呼び出す。このページの上部に、この関係を記した図がある。
これは、すべての適用可能メソッドを実行し、それらの返した値を足し合わせて全体の値として返すという結合方法である。同様のメソッド結合として標準で定義されているものに、codice_18,codice_19,codice_20などがある。
これは、すべての適用可能メソッドを実行し、それらの返した値の最大値を全体の値として返すという結合方法である。
同様のメソッド結合として標準で定義されているものにcodice_21がある。
codice_22は、ユーザーに、新たなメソッド結合を定義する自由を与える。
以下に、CLOSを用いて複数のクラスでメソッドを適用する例を示す.
動物、野生の犬、ペットの犬、人間、ステュワーデス、男というクラスを定義した。
それぞれの継承順に基づいて、何かを言わせてみる。
多重ディスパッチとcodice_18メソッドコンビネーションを用いて、何かが何かに出会った時の反応をリストにして書き出させてみる。