2

皆様、良い一日を…

私は自分の会社で複雑なプロジェクトに取り組んでおり、プロジェクトでいくつかのしわくちゃの工場設計パターンを使用しています。詳細は省略します。「リーダー」のみが作成できるいくつかのクラス(「デバイス」と呼びます)があります。

class DeviceBase // this is a virtual base class
{
  public:
   //some stuff
   friend class ReaderBase; // this is OK and necessary I guess?
  private:
   DeviceBase(); // cannot create a device directly
   //some more stuff
}

class Device1: public DeviceBase // some extended device
{
  public:
   //some stuff 
  private:
   //some more stuff
}

class Device2: public DeviceBase  // some other extended device
{
  public:
   //some stuff

  private:
   //some more stuff
}

現在、デバイスの工場である「リーダー」:

class ReaderBase
{
  private:
    DeviceBase[] _devices; // to keep track of devices currently "latched"
  public:
    // some other methods, getters-setters etc ...

    // this method will create the "Devices" :
    virtual bool PollforDevice ( DeviceType, timeout) = 0; 

}

さて、これが私のファクトリ クラスです。しかし、それは (ご覧のとおり) 純粋な仮想です。私はこれから特別なリーダーを継承しています:

 class InternalReader: public ReaderBase
 {
   public:
     // define other inherited methods by specifics of this reader
     bool PollforDevice( DeviceType dt, timeout ms)
     {
         switch(dt)
         {
           case Device1: { /* create new device1 and attach to this reader */ } break;
           case Device2: { /* create new device2 and attach to this reader */ } break;
         }
         // show goes on and on...
     }
 }

 class ExternalReader: public Reader
 {
   public:
     // define other inherited methods by specifics of this reader
     bool PollforDevice( DeviceType dt, timeout ms)
     {
         switch(dt)
         {
           case Device1: { /* create new device1 and attach to this reader */ } break;
           case Device2: { /* create new device2 and attach to this reader */ } break;
         }
         // show goes on and on...
     }
 }

私がこのパターンを使用する理由は、これらの「リーダー」を同時に複数接続できるシステムのために書いており、それらすべてを同時に使用する必要があるためです。

そして、これらの「デバイス」: 私は彼らのコンストラクターも公開することができ、誰もが幸せになるでしょう。しかし、それらがコードライター自身によって作成されていないことを確認したい(他のコーダーがそれを確認するため)

今質問:

  1. ReaderBase がフレンドであることをすべての「デバイス」で明示的に宣言する必要がありますか? または、ベース「DeviceBase」で宣言するだけで十分ですか?
  2. 「ReaderBase」から継承された「リーダー」がこれらのデバイスのフレンドでもあるすべての「デバイス」を明示的に配置する必要がありますか、それとも ReaderBase を配置するだけで十分ですか?
  3. 「ReaderBase」クラス全体をフレンドにする代わりに、メンバー メソッド「PollforDevice」だけをフレンドにすることはできますか? それが純粋な仮想メソッドであることを知っていると、継承されたコピーも友達になりますか?

質問が非常に長くなって申し訳ありませんが、明確にしたいだけです。

前もって感謝します...

4

2 に答える 2

2

のような純粋な抽象基底クラスの構築可能性について悩む必要はありDeviceBaseません。適切に設計されたコントラクトまたは抽象基本クラスである場合、とにかく構築できません。言及していないある種のフレームワークに適合する必要がない限り、非表示の反対を実行してください。

struct DeviceBase {
    virtual void Foo() = 0;
    virtual void Bar() = 0;
    virtual ~DeviceBase() = default;
};

ちなみに、コンストラクタまたはデストラクタprivateを宣言すると、非常に効果的にクラスが「封印」されます。何らかの理由DeviceBaseで が抽象的でない場合 (これは私の目には重大な設計上の欠陥でした)、コンストラクターprotectedを notにしprivateます。気にする必要があるのは、具象Deviceクラスのコンストラクターのアクセシビリティです。これらの実装クラスを「公開」する予定である (つまり、ライブラリのユーザーがそれらの定義にアクセスできる) と仮定し、直接構築が禁止されていることを強調したい場合は、「アクセス イディオム」(私が発明した名前) を使用します。

namespace impl_detail {
    class DeviceAccess;
}

class ConcreteDevice1 : public DeviceBase {
    friend class impl_detail::DeviceAccess;
    // implementation of DeviceBase and all other stuff go 
    // into the "private" section
};

namespace impl_detail {
    class DeviceAccess {
        template< class TDevice >
        static DeviceBase* Create()
        {
            return new TDevice;
        }
    };
};

あなたのReaderクラスでは、インスタンスimpl_detail::DeviceAccess::Createを構築するために使用します。Device

// Your ExternalReader::PollForDevice...
switch (dt) {
    case Device1:
        return impl_detail::DeviceAccess::Create<ConcreteDevice1>();
    case Device2: 
        // etc...
}

簡単に言えば、最善の解決策は、具体的な実装クラスをまったく公開しないことです.2番目に良いのは、上記の種類など、構築を制限するある種の「心理的障壁」です...

于 2013-01-16T10:04:17.370 に答える
1
  1. ReaderBase がフレンドであることをすべての「デバイス」で明示的に宣言する必要がありますか? または、ベース「DeviceBase」で宣言するだけで十分ですか?
  2. 「ReaderBase」から継承された「リーダー」がこれらのデバイスのフレンドでもあるすべての「デバイス」を明示的に配置する必要がありますか、それとも ReaderBase を配置するだけで十分ですか?

友情は (友情関係のどちらの側でも) 継承されないため、スキームが機能する唯一の方法は、すべての派生デバイスですべての派生リーダーの友情を宣言することです。これにより、Reader クラスと Device クラスの間に適切な設計ではない密結合が作成されます。

3) 「ReaderBase」クラス全体をフレンドにする代わりに、メンバー メソッド「PollforDevice」だけをフレンドにすることはできますか? それが純粋な仮想メソッドであることを知っていると、継承されたコピーも友達になりますか?

ReaderX::PollforDeviceクラス全体の代わりに友達を作ることもできますがReaderX、それはあまり役に立たず、解決が困難な循環依存関係への扉を開くだけです。


実際、階層 X のクラスが階層 Y のクラスによってのみ作成され、他の誰も作成できないような設計を作成することは、両方の階層のクラス間の密結合を作成せずに作成することは非常に困難です。私のアプローチは

  1. 何よりもまず、同僚に、 が必要なDeviceX場合は から入手でき、ReaderY他の方法では入手できないことを教育します。これがコード レビューで実施されていることを確認してください。他のすべての手順は、ダメージ コントロールです。
  2. クラスのみがBaseDeviceReader の実装外のコードに公開されるようにしてください。
  3. すべての Device クラスのデストラクタを保護します。これにより、Device クラスは派生クラスまたはフレンドによってのみクリーンアップできるようになります (そして、フレンド以外によるスタック割り当ては自動的に除外されます)。誤って Device クラスを直接使用しようとした場合は、よく考えてください。
  4. デバイスの実際のReaderBaseクリーンアップを行う機能をDeviceBase提供します。ReaderBaseこれは、デバイスを確実にクリーンアップできるようにするために必要です。
于 2013-01-16T08:41:41.213 に答える