21

PHPのマニュアルによると、次のようなクラスです。

abstract class Example {}

インスタンス化できません。インスタンスのないクラスが必要な場合、たとえばレジストリパターンの場合:

class Registry {}
// and later:
echo Registry::$someValue;

クラスを単純に抽象として宣言するのは良いスタイルと見なされますか?そうでない場合、抽象クラスと比較して、コンストラクターを保護されたメソッドとして非表示にすることの利点は何ですか?

質問の理由:私が見る限り、マニュアルでは抽象クラスをインスタンス化の可能性がある後のクラスの青写真のように参照しているため、機能が少し乱用される可能性があります。

更新:まず第一に、すべての答えに感謝します!しかし、多くの答えはまったく同じように聞こえます。「抽象クラスをインスタンス化することはできませんが、レジストリの場合は、シングルトンパターンを使用しないのはなぜですか?」

残念ながら、それは多かれ少なかれ私の質問の繰り返しでした。シングルトンパターン(別名非表示)を使用することの利点は、それを__construct()宣言するだけでそれabstractについて心配する必要がないことと比較して何ですか?abstract(たとえば、クラスが実際に使用されていないなど、開発者間の強い意味合いです。)

4

10 に答える 10

19

クラスが何らかのスーパータイプを定義することを意図していない場合は、として宣言するべきではありませんabstract

あなたの場合、私はむしろクラスに行きたいです:

  • __constructおよび__cloneをプライベート メソッドとして 定義する
    • そのため、クラスを外部からインスタンス化することはできません
  • そして、このようにして、クラスはそれ自体のインスタンスを作成できます


では、なぜ静的メソッドだけでなくシングルトンを使用するのでしょうか? 少なくともいくつかの理由が有効である可能性があると思います。

  • シングルトンを使用するということは、クラスのインスタンスを使用することを意味します。非シングルトン クラスをシングルトン クラスに簡単に変換できます。make__construct__cloneprivate を実行し、いくつかのgetInstanceメソッドを追加するだけで済みます。
  • シングルトンを使用すると、通常のインスタンスで使用できるすべてのものにアクセスできることも意味します: $this、プロパティ、...
  • ああ、3番目のもの(それについてはわかりませんが、重要性があるかもしれません):PHP <5.3では、静的メソッド/データの可能性が少なくなります:
  • Late Static Bindingは PHP 5.3 でのみ追加されました。静的メソッド/クラスを使用する場合、それがないとしばしば難しくなります。特に継承を使用する場合。


そうは言っても、次のようなコードがあります。

abstract class MyClass {
    protected static $data;
    public static function setA($a) {
        self::$data['a'] = $a;
    }
    public static function getA() {
        return self::$data['a'];
    }
}

MyClass::setA(20);
var_dump(MyClass::getA());

うまくいきます...しかし、それはあまり自然に感じられません...そしてこれは非常に単純な例です(Late Static Bindingと魔法のメソッドで前に述べたことを参照してください) .

于 2010-03-22T17:38:29.433 に答える
3

あなたが記述したことは PHP 言語で許可されていますが、それはabstractクラスの意図された使用法ではありません。クラスstaticのメソッドは使用しません。abstract

これを行うことの欠点は次のとおりです。別の開発者が抽象クラスを拡張し、オブジェクトをインスタンス化する可能性があります。これは回避したいことです。例:

class MyRegistry extends AbstractRegistry { }
$reg = new MyRegistry();

確かに、意図した使用法に準拠しない別の開発者に抽象クラスを引き渡す場合にのみ、これについて心配する必要がありますが、それがクラスをシングルトンにする理由でもあります。非協力的な開発者は、プライベート コンストラクターをオーバーライドできます。

class Registry
{
  private function __construct() { }
}

class MyRegistry extends Registry
{
  public function __construct() { } // change private to public
}

このクラスを自分で使用している場合は、クラスをインスタンス化しないことを忘れないでください。そうすれば、それを防ぐためにどちらのメカニズムも必要ありません。したがって、これを他の人が使用するように設計しているため、それらの人々が意図した使用法を回避できないようにする何らかの方法が必要です。

そこで、次の 2 つの代替案を提示します。

  1. シングルトン パターンに固執し、コンストラクターも同様であることを確認して、final誰もクラスを拡張してコンストラクターを非プライベートに変更できないようにします。

    class Registry
    {
      private final function __construct() {
      }
    }
    
  2. レジストリが静的とオブジェクトの両方の使用をサポートするようにします。

    class Registry
    {
      protected static $reg = null;
    
      public static function getInstance() {
        if (self::$reg === null) {
          self::$reg = new Registry();
        }
        return self::$reg;
      }
    }
    

    次にRegistry::getInstance()、静的に呼び出すnew Registry()か、オブジェクト インスタンスが必要な場合に呼び出すことができます。

    その後、グローバル レジストリ内に新しいレジストリ インスタンスを保存するなど、気の利いたことができます。:-)

    これを Zend Framework の一部として実装しました。Zend_Registry

于 2010-04-17T01:33:58.083 に答える
1

オブジェクト指向には、一般的でよく知られているパターンがあります。型にはまらない方法で使用abstractすると、混乱を招く可能性があります (申し訳ありませんが、私のいくつかの例は PHP ではなく Java で書かれています):

  • 抽象クラス- 共通の祖先を概念化することを意図したクラスですが、その実際のインスタンスは存在することを意図していません (たとえば、形状は四角形と三角形の抽象スーパークラスです)。
    一般的に実装されるのは:
    • クラスで修飾子を使用abstractして直接インスタンス化を防ぎますが、クラスからの派生は許可します
  • ユーティリティ クラス- ソリューション スペース内のオブジェクトを表すのではなく、Java の Math クラスなど、有用な静的操作/メソッドのコレクションであるクラス。
    一般的に実装されるのは:
    • クラスを派生不可能にします。たとえば、Java はfinalクラスで修飾子を使用します。
    • 直接インスタンス化を防止する - コンストラクターを提供せず、暗黙的またはデフォルトのコンストラクター (およびコピー コンストラクター) を非表示または無効にする
  • シングルトン クラス- ソリューション スペース内のオブジェクトを表すクラスですが、多くの場合、インスタンスが 1 つだけであることを保証するためにインスタンス化が制御または制限されます。
    一般的に実装されるのは:
    • クラスを派生不可能にします。たとえば、Java はfinalクラスで修飾子を使用します。
    • 直接インスタンス化を防止する - コンストラクターを提供せず、暗黙的またはデフォルトのコンストラクター (およびコピー コンストラクター) を非表示または無効にする。
    • インスタンスを取得するための特定の手段を提供する -getInstance()唯一のインスタンスまたは限られた数のインスタンスのうちの 1 つを返す静的メソッド (多くの場合)
于 2010-04-20T20:28:43.493 に答える
1

他の人が言ったように、抽象クラスをインスタンス化することはできません。クラスで静的メソッドを使用してインスタンス化を防ぐこともできますが、適切な理由がない限り、私はそうするのが好きではありません。

今は少し話題から外れているかもしれませんが、あなたの例では、レジストリパターンクラスにこれが欲しいと言っていました。インスタンス化したくない理由は何ですか?使用するレジストリごとに Registry のインスタンスを作成したほうがよいのではないでしょうか?

何かのようなもの:

class Registry {
    private $_objects = array( );

    public function set( $name, $object ) {
        $this->_objects[ $name ] = $object;
    }

    public function get( $name ) {
        return $this->_objects[ $name ];
    }
}

この場合、Singleton も使用しません。

于 2010-03-22T18:07:45.193 に答える
1

静的プロパティ/メソッドのみを定義するクラスを抽象に設定しても、実際の効果はありません。クラスを拡張してインスタンス化し、メソッドを呼び出すと、静的クラス プロパティが変更されます。明らかに非常に混乱しています。

アブストラクトも誤解を招きます。抽象は、いくつかの機能を実装するクラスを定義することを目的としていますが、適切に機能するには、(継承によって追加された) より多くの動作が必要です。その上、これは通常、static ではまったく使用すべきではない機能です。あなたは事実上、プログラマーにそれを間違って使用するように勧めています。

簡単な答え: プライベート コンストラクターは、より表現力があり、フェイル セーフになります。

于 2010-04-17T16:32:44.427 に答える
0

私の理解では、インスタンスのないクラスは、OOPプログラムで使用すべきではないものです。クラスの全体(および唯一の)目的は、新しいオブジェクトの青写真として機能することだからです。Registry::$someValueとの唯一の違い$GLOBALS['Registry_someValue']は、前者は「より派手」に見えることですが、どちらの方法も実際にはオブジェクト指向ではありません。

したがって、質問に答えるには、「シングルトンクラス」は必要ありません。オプションで、ファクトリメソッドを備えたシングルトンオブジェクトが必要です。

class Registry
{
    static $obj = null;

    protected function __construct() {
        ...
    }

    static function object() {
        return self::$obj ? self::$obj : self::$obj = new self;
    }
}

...

Registry::object()->someValue;

明らかabstractにここでは機能しません。

于 2010-04-19T22:08:39.653 に答える
0

abstract実際には、クラス継承の「青写真」と呼ばれるものを示すことを意図しています。

レジストリは通常、シングルトン パターンに従います。つまり、レジストリはプライベート変数でインスタンス化されます。として定義するとabstract、これが機能しなくなります。

于 2010-03-22T17:40:48.643 に答える
0

それは習慣をコーディングすることの問題だと思います。抽象クラスについて考えるとき、それは通常、使用するためにサブクラス化する必要があるものです。したがって、クラスの抽象を宣言することは直感に反します。

それ以外は、シングルトンとして実装する場合は $this->somevar ではなく、抽象化する場合はメソッドで self::$somevar を使用するだけの問題です。

于 2010-04-20T08:44:22.667 に答える
0

私は抽象クラスを使用しません。あなたが提案するように、保護された/プライベートコンストラクターでシングルトンに似たものを使用します。$instance実際のレジストリ インスタンス以外の静的プロパティはほとんどありません。最近、次のような Zend Framework の典型的なパターンのファンになりました。

class MyRegistry {

  protected static $instance = null;

  public function __construct($options = null)
  {
  }

  public static function setInstance(MyRegistry $instance)
  {
    self::$instance = $instance;
  }

  public static function getInstance()
  {
     if(null === self::$instance) {
        self::$instance = new self;
     }

     return self::$instance;
  }
}

このようにして、基本的にシングルトンを取得しますが、使用する構成済みインスタンスを注入できます。これは、テスト目的と継承に便利です。

于 2010-03-22T17:39:05.003 に答える
0

抽象クラスの目的は、1) 他のクラスにとって意味があり、2) それらのクラスのいずれかのコンテキストにない場合は意味がないメソッドを定義することです。

いくつかの php ドキュメントを言い換えるために、データベースに接続していると想像してください。接続する特定の種類のデータベースがない限り、データベースに接続してもあまり意味がありません。それでも、接続は、データベースの種類に関係なくやりたいことです。したがって、接続は抽象データベース クラスで定義して継承し、たとえば MYSQL クラスによって意味のあるものにすることができます。

あなたの要件から、これを行うつもりはないように思えますが、代わりに単にインスタンスのないクラスを必要とします。抽象クラスを使用してこの動作を強制することもできますが、これは抽象クラスの目的を悪用するため、私にはハックに思えます。抽象クラスに遭遇した場合、たとえば、これにはいくつかの抽象メソッドがあると合理的に期待できるはずですが、クラスには何もありません。

したがって、シングルトンがより良い選択肢のようです。

ただし、インスタンスを持たないクラスが必要な理由が単にどこでも呼び出せるようにするためである場合、そもそもなぜクラスを持っているのでしょうか? すべての変数をグローバルとしてロードして、クラス経由ではなく直接呼び出すことができるのはなぜですか?

これを行う最善の方法は、クラスをインスタンス化してから、依存性注入で渡すことだと思います。あなたがそれをするのが面倒なら (そして、あなたが怠けているなら、それはあなたのコードであり、私のものではありません)、クラスをまったく気にしないでください。

更新: 2 つのニーズの間で競合しているようです: 物事を迅速に行う必要性と、物事を正しい方法で行う必要性です。時間を節約するために大量のグローバル変数を持ち運ぶことを気にしない場合は、入力が少なくて済むため、シングルトンよりも抽象を使用することを好むと思われます。どちらの必要性があなたにとってより重要かを選び、それに固執してください。

ここでの正しい方法は、シングルトンや抽象クラスを使用せず、代わりに依存性注入を使用することです。手っ取り早い方法は、大量のグローバルまたは抽象クラスを用意することです。

于 2010-04-14T22:37:57.400 に答える