2011-09-12 31 views
1

我迷路了,需要一些神圣的指导。对抽象层次的具体实例执行合同

首先第一件事情:假设你有一些很好的,整齐的接口:

class IProduct 
{ 
public: 
    virtual void DoThings(); 
} 


enum ProductType 
{ 
    ... 
} 


class IProducer 
{ 
public: 
    virtual IProduct* Produce(ProductType type); 
} 


class IConsumer 
{ 
public: 
    virtual void Consume(IProduct* product); 
} 

它简单简单而:抽象工厂,抽象的消费者谁将会调用接口,通过这些新鲜催生IProducts乐意提供。 但是棘手的部分来了。 假设有两个(或更多个)并行混凝土基团:

class ConcreteProducerA : public IProducer { ... } 
class ConcreteConsumerA : public IConsumer { ... } 
class ConcreteProductA : public IProduct { ... } 

class ConcreteProducerB : public IProducer { ... } 
class ConcreteConsumerB : public IConsumer { ... } 
class ConcreteProductB : public IProduct { ... } 

而那些混凝土是reeealy不同的事情。像航天飞机部件(带零件工厂和班车组装线)和蔬菜袋(有农场和.. idk,谁会消费那些蔬菜?)。然而,他们一般都有这样的事情:DoThings()。不管你喜欢什么,假装它就像PackAndSend()或Serialize()或Dispose()一样。没有什么具体的东西,但基于层次结构的合法性。 但这些仍然有更多的差异,比普遍。所以那些具体的消费者倾向于以不同的方式使用它们。换句话说,事实上,他们绝对必须确定它是假定的具体类型。

所以问题在于:我迫使该层次结构的用户在其虚拟覆盖中立即将IPoduct向下压缩为Con​​creteProduct。这就让我苦恼。我觉得我错过了一些东西:层次结构存在很大缺陷,缺乏模式知识,有些东西。 我的意思是,我可以确定,ConcreteConsumerB总是收到ConcreteProductB,但它仍然是一个沮丧。你会不会使用一个框架,总是绕过(void *)的,并迫使你把它转换成你认为会来的亚呢?

解决方案我已经考虑:

  1. 隧道所有conctrete interfeces到IProduct。但是那个产品变成了无法控制的blob,谁可以吃(),BeEaten(),Launch(),Destroy()以及谁知道还有什么。所以这个解决方案似乎没什么比向我下传更好。
  2. DoThings()可能可以从IProduct解耦成另一个处理程序,它将能够接受所有的混凝土(类似Visitor)。这样IP产品可以被删除,并且会有单独的具体组。但是如果有一个SemiConcrete层,它为这些具体的组提供了一些通用功能呢?就像标签,变形,按摩,不管。此外,当需要添加另一个具体组时,我将被迫更改该访问者,这会增加耦合度。
  3. (ab)使用模板。这在目前看来是明智的。东西沿线

    template < typename _IProduct > 
    class IConcreteProducer : public IProducer 
    { 
    public: 
        virtual _IProduct* Produce(_IProduct::Type type) = 0; 
        virtual _IProduct::Type DeduceType(ProductType type) = 0; 
        virtual IProduct* Produce(ProductType type) 
        { 
         return IConcreteProducer<typename _IProduct>::Produce(DeduceType(type)); 
        } 
    } 
    
    template < typename _IProduct > 
    class IConcreteConsumer : public IConsumer 
    { 
    public: 
        virtual void Consume(_IProduct* product) = 0; 
        virtual void Consume(IProduct* product) 
        { 
         IConcreteConsumer<typename _IProduct>::Consume((_IProduct*)product); 
        } 
    } 
    

    这样我就控制了那个低调,但它是stil目前。

不管怎样,这个问题听起来很熟悉吗?有人看到它解决了,或者英雄解决了它自己? C++解决方案会很棒,但我认为任何静态类型的语言就足够了。

+0

如果他们是*“reeealy不同的东西”*是一个继承层次结构和工厂丰盛真的是工作的正确工具? – Flexo

+0

这显然是错误的:'IConcreteConsumer <类型名称_IProduct>' – Nawaz

+0

@awoodland以及它们的区别需要垂头丧气,但有足够的在共同待从一些常见的碱衍生至少为代码复用(除了语义)的缘故。除了派生(还保留了OOP风格)之外,还有另外一种方式来优雅地重用它吗? – Dark

回答

2

然而,他们普遍认为的事情:DoThings()。假设它是, 就像,PackAndSend()或Serialize()或Dispose(),无论你喜欢什么。 没有具体的东西,但基于层次结构是合法的。

就因为他们可以可以在某些层次,并不意味着他们应该。他们是无关的。我甚至无法理解你通过推广班车和蔬菜来增加任何代码基础的价值。如果它不会给用户带来好处,那么你可能会让自己变得更加复杂。

我希望看到类似下面的界面。注意它们不会从任何东西继承。如果你有共享的代码,编写简单的傻瓜具体的类,人们可以通过组合重用。

template<typename T> 
    class Producer { 
    public: 
    virtual ~Producer() {} 
    virtual std::auto_ptr<T> produce() = 0; 
    }; 

template<typename T> 
    class Consumer { 
    public: 
    virtual ~Consumer() {} 
    virtual void consume(std::auto_ptr<T> val) = 0; 
    }; 

然后,我期望看到具体的功能,从各种来源创建这些。

typedef Producer<Shuttle> ShuttleProducer; 
typedef Consumer<Shuttle> ShuttleConsumer; 

std::auto_ptr<ShuttleProducer> GetShuttleProducerFromFile(...); 
std::auto_ptr<ShuttleProducer> GetShuttleProducerFromTheWeb(...); 
std::auto_ptr<ShuttleProducer> GetDefaultShuttleProducer(); 

有可能没有你想做的模式,它可能是两种模式,你smooshing(技术术语)在一起。你没有背叛为什么这些东西应该共享一个代码库,所以我们只能猜测。

在更复杂的情况,你会想从创建严格分开使用,但。有不同的接口看起来有些类似,但用法不同,这是完全有效的。

class Foo { 
public: 
    virtual ~Foo() {} 
    virtual void doStuff() = 0; 
    virtual void metamorphose() = 0; 
}; 

class Fu { 
public: 
    virtual ~Fu() {} 
    virtual void doStuff() = 0; 
    virtual void transmorgrify() = 0; 
}; 
2

一种可能性是将第二层引入热层级。从IProduct导出IShuttle,并从中派生出该组。然后添加一个IShuttleProducer,产生一个IShuttle*而不是IProduct*。这没关系,因为C++允许虚函数的协变返回类型......只要新的返回类型来自原始,它仍然被认为是覆盖。

但是你的设计可能需要一些反思两种方式。