同じクラスを共有する2つ以上のオブジェクトのポイントは、動作が同じ、つまり、メソッドが同一であることです。
ただし、フィールドに異なる値を割り当てることができるのと同じ方法でオブジェクトのメソッドを再定義できる言語がOOPある場合、その結果はオブジェクトの構築になります。同じクラスからはまったく同じ動作を示さなくなりました。
私が間違っていなければ、このJavaScriptを実行できますか?この質問と一緒に、なぜ誰かがこれをしたいのでしょうか?
ほとんどの(クラスベースの)メソッドのメソッドOOP言語はタイプごとに固定されています。
JavaScriptはクラスベースではなくプロトタイプベースであり、「クラス」とオブジェクトの間に明確な区別がないため、インスタンスごとにメソッドをオーバーライドできます。実際には、JavaScriptの「クラス」は、インスタンスの動作方法のテンプレートのようなオブジェクトです。
ファーストクラスの関数、Scala、Java 8、C#(デリゲートを介して)など)を許可する任意の言語は、インスタンスごとのメソッドのオーバーライドのように機能できます。フィールドをフィールドで定義する必要があります。関数タイプを入力し、各インスタンスでオーバーライドします。
Scalaには別の可能性があります。 Scalaでは、(クラスキーワードの代わりにオブジェクトキーワードを使用して)オブジェクトシングルトンを作成できるため、クラスを拡張してメソッドをオーバーライドし、オーバーライドを使用してその基本クラスの新しいインスタンスを作成できます。
なぜこれを行うのですか?何十もの理由が考えられます。動作は、さまざまなフィールドの組み合わせを使用する場合よりも厳密に定義する必要があるかもしれません。また、コードを分離してより適切に整理することもできます。ただし、一般に、これらのケースはまれであり、フィールド値を使用したより簡単な解決策がしばしばあると思います。
インスタンスごとのメソッドを提供する言語を要求しました。 Javascriptの答えはすでにあるので、EQLスペシャライザを使用できるCommon LISPでそれがどのように行われるかを見てみましょう。
;; define a class
(defclass some-class () ())
;; declare a generic method
(defgeneric some-method (x))
;; specialize the method for SOME-CLASS
(defmethod some-method ((x some-class)) 'default-result)
;; create an instance named *MY-OBJECT* of SOME-CLASS
(defparameter *my-object* (make-instance 'some-class))
;; specialize SOME-METHOD for that specific instance
(defmethod some-method ((x (eql *my-object*))) 'specific-result)
;; Call the method on that instance
(some-method *my-object*)
=> SPECIFIC-RESULT
;; Call the method on a new instance
(some-method (make-instance 'some-class))
=> DEFAULT-RESULT
EQLスペシャライザは、ディスパッチの対象となる引数が、eql
が意味を持つ型(数値、記号など)を持っていると想定されている場合に役立ちます。一般的には、それは必要なく、単に問題に必要なだけのサブクラスを定義します。ただし、場合によっては、たとえばシンボルであるパラメータに従ってディスパッチする必要があるだけです。case
式は、ディスパッチ関数の既知のケースに制限されますが、メソッドはいつでも追加および削除できます。 。
また、インスタンスの特殊化は、実行中のアプリケーションの特定のオブジェクトで何が起こっているのかを一時的に検査する場合に、デバッグの目的で役立ちます。
あなたの質問の動機を推測するのは難しいので、いくつかの可能な答えはあなたの本当の興味に対処するかもしれないし、しないかもしれません。
一部の非プロトタイプ言語でも、この効果を概算することが可能です。
たとえば、Javaでは、匿名の内部クラスはあなたが説明しているものにかなり近いです-必要なメソッドだけをオーバーライドして、オリジナルのサブクラスを作成してインスタンス化できます。結果のクラスはinstanceof
の元のクラスになりますが、同じクラスにはなりません。
なぜこれをしたいのですか? Java 8ラムダ式を使用すると、最良のユースケースの多くがなくなると思います。以前のバージョンのJavaでは、少なくとも、これは簡単で狭義のクラスの急増を回避できます。それはつまり、関連するユースケースが多数あり、ごくわずかな機能上の違いしかない場合、ほとんどオンザフライで(ほぼ)作成でき、必要なときに動作の違いが注入されます。
そうは言っても、J8より前でも、これをリファクタリングして差を1つまたは3つのフィールドにシフトし、コンストラクターに注入することができます。もちろん、J8を使用すると、メソッド自体をクラスに注入できますが、別のリファクタリングのほうがきれいな場合(それほどクールではない場合)に、そうしたい場合があります。
シングルトンオブジェクトを使用してRubyでこれを行うこともできます:
class A
def do_something
puts "Hello!"
end
end
obj = A.new
obj.do_something
def obj.do_something
puts "Hello world!"
end
obj.do_something
生成する:
Hello!
Hello world!
用途に関しては、これは実際にRubyがクラスおよびモジュールのメソッドを実行する方法です。たとえば:
def SomeClass
def self.hello
puts "Hello!"
end
end
実際には、hello
オブジェクトClass
にシングルトンメソッドSomeClass
を定義しています。
インスタンスごとのメソッドは、実行時に独自のクラスをアセンブルできると考えることができます。これにより、2つのクラスを1つにまとめて相互にやり取りする以外に目的のない多くのグルーコードを排除できます。 Mixins は、同じ種類の問題に対する多少構造化されたソリューションです。
blub paradox に少し苦しんでいます。本質的に、実際のプログラムでその機能を使用するまで、その機能の値を確認することは困難です。だから、それがうまくいくと思う機会を探し、それを試して、何が起こるかを見てください。
1つのメソッドのみが異なるクラスのグループのコードを調べます。他の2つのクラスを異なる組み合わせで組み合わせることが唯一の目的であるクラスを探します。他に何もしないで他のオブジェクトに呼び出しを渡すメソッドを探します。複雑な 作成パターン を使用してインスタンス化されたクラスを探します。これらはすべて、インスタンスごとのメソッドに置き換えられる潜在的な候補です。
他の回答は、これが動的オブジェクト指向言語の一般的な機能である方法、およびファーストクラスの関数オブジェクト(c#のデリゲート、c ++の演算子()をオーバーライドするオブジェクトなど)を持つ静的言語で簡単にエミュレートできる方法を示しています。そのような関数を持たない静的言語では、それはより困難ですが、Strategyパターンと、その実装を戦略に委譲するだけの方法の組み合わせを使用することによって実現できます。これは事実上、デリゲートを使用してC#で行うのと同じことですが、構文は少し面倒です。
それが良いことだと言っているわけではありませんが、これはPythonで簡単に可能です。頭に浮かぶ良いユースケースはありませんが、確かに存在します。
class Foo(object):
def __init__(self, thing):
if thing == "Foo":
def print_something():
print "Foo"
else:
def print_something():
print "Bar"
self.print_something = print_something
Foo(thing="Foo").print_something()
Foo(thing="Bar").print_something()
C#や他のほとんどの同様の言語で、このようなことを行うことができます。
public class MyClass{
public Func<A,B> MyABFunc {get;set;}
public Action<B> MyBAction {get;set;}
public MyClass(){
//todo assign MyAFunc and MyBAction
}
}
概念的には、Javaのような言語では、クラスのすべてのインスタンスが同じメソッドを持っている必要がありますが、インダイレクションの追加のレイヤーを追加することで、それらがそうでないかのように見せることは可能です。おそらくネストされたクラスと組み合わせて。
たとえば、クラスFoo
が、メソッドquack(Foo)
を含む静的抽象ネストクラスQuackerBase
と、QuackerBase
、それぞれ独自のquack(Foo)
の定義がある場合、外部クラスにタイプquacker
のフィールドQuackerBase
がある場合、そのフィールドを設定して、 (おそらくシングルトン)ネストされたクラスのいずれかのインスタンス。その後、quacker.quack(this)
を呼び出すと、そのフィールドにインスタンスが割り当てられているクラスのquack
メソッドが実行されます。
これはかなり一般的なパターンであるため、Javaには、適切なタイプを自動的に宣言するメカニズムが含まれています。このようなメカニズムは、仮想メソッドとオプションでネストされた静的メソッドを使用するだけでは実行できないことを実際に行っていませんクラスですが、他のクラスに代わって単一のメソッドを実行することを唯一の目的とするクラスを作成するために必要なボイラープレートの量を大幅に減らします。
私はそれがRuby、groovy、Javascript(および他の多くの多く)のような「動的」言語の定義だと思います。動的とは、(少なくとも部分的に)クラスインスタンスがその場で動作する方法を動的に再定義する機能を指します。
これは素晴らしいOO一般的な練習ではありませんが、多くの動的言語プログラマにとってOO原則は最優先事項ではありません。
Monkey-Patchingのようないくつかのトリッキーな操作を簡素化し、クラスインスタンスを調整して、予期しない方法で閉じたライブラリとやり取りできるようにします。