同じクラスから構築されたオブジェクトは、一意のメソッド定義を持つことができますか?


14

同じクラスを共有する2つ以上のオブジェクトのポイントは、振る舞いが同じ、つまりメソッドが同一であるということなので、これは奇妙な質問のように思えます。

ただし、フィールドに異なる値を割り当てるのと同じ方法でオブジェクトのメソッドを再定義できるOOP言語があるかどうか興味があります。結果は、まったく同じ動作を示さなくなった同じクラスから構築されたオブジェクトになります。

私が間違っていなければ、このJavaScriptを実行できますか?この質問とともに、なぜ誰かがこれをしたいのでしょうか?


9
専門用語は「プロトタイプベース」です。それを検索すると、このOOPのフレーバーに関する多くの資料が得られます。(多くの人々は、そのような構造を適切な「クラス」とは見なしていないことに注意してください。正確な属性とメソッドがないためです。)
Kilian Foth 14年

1
ボタンの2つの異なるインスタンスがクリックされたときに、それぞれ異なるボタンハンドラーに接続されているため、異なる動作をする方法を意味しますか?もちろん、常に同じことを行うと主張することができます-彼らはボタンハンドラを呼び出します。とにかく、言語がデリゲートまたは関数ポインターをサポートしている場合、これは簡単です-デリゲートまたは関数ポインターを保持するプロパティ/フィールド/メンバー変数があり、メソッドの実装がそれを呼び出して、あなたは去ります。
ケイトグレゴリー14年

2
複雑です:)スリング関数の参照が一般的な言語では、setClickHandler()メソッドにコードを渡し、同じクラスの異なるインスタンスに非常に異なることをさせるのは簡単です。便利なラムダ式を持たない言語では、新しいハンドラーのためだけに新しい匿名サブクラスを作成する方が簡単です。従来、オーバーライドメソッドは新しいクラスの特徴であると見なされていましたが、属性の値を設定することはそうではありませんでしたが、特にハンドラー関数では、この2つの効果は非常に似ているため、区別は言葉に関する戦争になります。
キリアンフォス14年

1
まさにあなたが求めているものではありませんが、これは戦略設計パターンのように聞こえます。ここには、実行時に型を効果的に変更するクラスのインスタンスがあります。それがこれを可能にする言語が、言及する価値はありませんので、あなたが「なぜ誰かがこれをしたいと思う」と言ったのでそれは少しオフトピックだ
トニー・

@Killian ForthプロトタイプベースのOOPは、定義によりクラスを持たないため、OPが求めているものと完全には一致しません。主な違いは、クラスがインスタンスではないことです。
eques

回答:


9

ほとんどの(クラスベースの)OOP言語のメソッドは、タイプごとに固定されています。

JavaScriptはクラスベースではなくプロトタイプベースであるため、「クラス」とオブジェクトの間に明確な区別がないため、インスタンスごとにメソッドをオーバーライドできます。実際には、JavaScriptの「クラス」は、インスタンスの動作方法のテンプレートに似たオブジェクトです。

Scala、Java 8、C#(デリゲート経由)などのファーストクラス関数を許可する言語は、インスタンスごとのメソッドオーバーライドがあるように動作できます。関数タイプでフィールドを定義し、各インスタンスでそれをオーバーライドする必要があります。

Scalaには別の可能性があります。Scalaでは、(クラスキーワードの代わりにオブジェクトキーワードを使用して)オブジェクトシングルトンを作成できるため、クラスを拡張し、メソッドをオーバーライドして、そのベースクラスの新しいインスタンスをオーバーライドできます。

なぜ誰かがこれを行うのでしょうか?いくつかの理由が考えられます。さまざまなフィールドの組み合わせを使用するよりも、動作を厳密に定義する必要があるかもしれません。また、コードの分離と編成を改善することもできます。ただし、一般的には、これらのケースはまれであり、フィールド値を使用したより簡単なソリューションがあることが多いと思います。


最初の文は意味がありません。クラスベースのOOPは固定型と何の関係がありますか?
ベルギ14年

タイプごとにメソッドのセットと定義が固定されている、つまりメソッドを追加または変更するには、新しいタイプを作成する必要があることを意味します(たとえば、サブタイプによって)。
eques

@Bergi:クラスベースのOOPでは、「クラス」と「タイプ」という言葉は本質的に同じことを意味します。整数型に値「hello」を持たせることは意味をなさないため、Employee型にEmployeeクラスに属さない値またはメソッドを持たせることも意味がありません。
スリーブマン14

@slebetman:クラスベースのOOP言語には、強力で静的に強制される型付けが必ずしも必要ではないことを意味しました。
ベルギ14

@Bergi:上記の答えの「ほとんど」の意味です(ほとんどはすべてではないことを意味します)。私はあなたの「何をしなければならないか」というコメントにコメントしているだけです。
スリーブマン14

6

あなたの質問の動機を推測するのは難しいので、いくつかの可能な答えはあなたの本当の興味に対処するかもしれません。

一部の非プロトタイプ言語でも、この効果を近似することが可能です。

たとえば、Javaでは、匿名の内部クラスは、記述しているものに非常に近い-元のサブクラスを作成およびインスタンス化して、目的のメソッドだけをオーバーライドできます。結果のクラスはinstanceof元のクラスになりますが、同じクラスにはなりません。

なぜこれをしたいのか?Java 8ラムダ式では、多くの最良のユースケースはなくなると思います。少なくともJavaの以前のバージョンでは、これにより、些細な用途の狭いクラスの急増を回避できます。つまり、関連するユースケースが多数あり、機能がわずかに異なるだけの場合、必要なときに動作の違いを注入して、ほぼオンザフライで(ほぼ)作成できます。

とはいえ、J8より前でも、これをリファクタリングして、違いを1つまたは3つのフィールドにシフトし、コンストラクターに注入することができます。もちろん、J8では、メソッド自体をクラスにインジェクトでき​​ますが、別のリファクタリングがよりクリーンな場合(そうでない場合)にそうする誘惑があるかもしれません。


6

インスタンスごとのメソッドを提供する言語を要求しました。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-specializersは、ディスパッチの対象となる引数にeql意味のある型(数字、記号など)がある場合に便利です。一般的に言えば、必要はなく、サブクラスをできるだけ多く定義する必要があります。あなたの問題に必要です。ただし、場合によっては、たとえばシンボルなどのパラメータに従ってディスパッチするだけで済みます。case式はディスパッチ関数の既知のケースに制限されますが、メソッドはいつでも追加および削除できます。

また、実行中のアプリケーション内の特定のオブジェクトで何が起こるかを一時的に検査する場合、インスタンスの特殊化はデバッグの目的に役立ちます。


5

また、シングルトンオブジェクトを使用して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


モジュールと拡張メソッドで答えを拡張しますか?(新しいメソッドでオブジェクトを拡張する他の方法を示すためだけに)?
knut 14年

@knut:extendオブジェクトのシングルトンクラスを実際に作成し、そのモジュールをシングルトンクラスにインポートするだけだと確信しています。
Linuxios 14年

5

インスタンスごとのメソッドは、実行時に独自のクラスをアセンブルできると考えることができます。これにより、2つのクラスをまとめて互いに通信する以外の目的のない、多くのグルーコードを削除できます。 ミックスインは、同じ種類の問題に対する多少構造化されたソリューションです。

あなたは本物のプログラムでその機能を使用するまで、本質的に言語機能の価値を見ることは難しいという、ブラブのパラドックスから少し苦しんでいます。それで、あなたがそれがうまくいくと思う機会を探し、それを試して、何が起こるか見てください。

1つのメソッドのみが異なるクラスのグループをコードで探します。他の2つのクラスを異なる組み合わせで結合することが唯一の目的であるクラスを探します。他に何もせずに呼び出しを別のオブジェクトに渡すメソッドを探します。複雑な作成パターンを使用してインスタンス化されるクラスを探します。これらはすべて、インスタンスごとのメソッドに置き換えられる潜在的な候補です。


1

他の回答は、これが動的オブジェクト指向言語の一般的な機能であり、ファーストクラスの関数オブジェクト(c#のデリゲート、c ++のoperator()をオーバーライドするオブジェクトなど)を含む静的言語で簡単にエミュレートできる方法を示しています。そのような機能を持たない静的言語では、それはより困難ですが、戦略パターンと、その実装を戦略に単に委任するメソッドの組み合わせを使用することで達成できます。これは実質的にデリゲートを使用してc#で行うことと同じですが、構文は少し面倒です。


0

このようなことは、C#および他のほとんどの類似言語で行うことができます。

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}

1
プログラマは、概念的な質問と回答は、物事を説明することが期待されています。説明の代わりにコードダンプを投げることは、IDEからホワイトボードにコードをコピーするようなものです。見た目はよく、時には理解できるかもしれませんが、奇妙に感じます...ただ奇妙です。コンパイラはありませんホワイトボード
ブヨ

0

概念的には、Javaのような言語ではクラスのすべてのインスタンスが同じメソッドを持っている必要がありますが、ネストされたクラスと組み合わせて、間接層を追加することで、あたかもそうでないように見せることができます。

たとえば、メソッドが含まFooれる静的な抽象ネストされたクラスQuackerBasequack(Foo)、から派生するいくつかの静的なネストされたクラスをQuackerBase定義できるquack(Foo)場合、それぞれがの独自の定義を持つ場合、外側のクラスquackerにtypeのフィールドがあるQuackerBase場合、そのフィールドを設定して、ネストされたクラスのいずれかのインスタンス(おそらくシングルトン)を識別します。呼び出しが完了すると、インスタンスがそのフィールドに割り当てられているクラスquacker.quack(this)quackメソッドが実行されます。

これはかなり一般的なパターンであるため、Javaには適切な型を自動的に宣言するメカニズムが含まれています。このようなメカニズムは、単に仮想メソッドとオプションで入れ子になった静的クラスを使用するだけでは実行できないことを実際には実行しませんが、単一のメソッドを実行することを唯一の目的とするクラスを生成するために必要な定型の量を大幅に削減します別のクラス。


0

それがルビー、グルービー、Javascript(およびその他多くの)のような「動的」言語の定義だと思います。動的とは、(少なくとも部分的に)クラスインスタンスの動作を動的に再定義する機能を指します。

一般にオブジェクト指向の優れたプラクティスではありませんが、多くの動的言語プログラマーにとってオブジェクト指向の原則は最優先事項ではありません。

Monkey-Patchingのようないくつかのトリッキーな操作を単純化し、クラスインスタンスを微調整して、予期しない方法で閉じたライブラリとやり取りできるようにします。


0

良いことだと言っているわけではありませんが、これは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()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.