设计模式:处理类似实体的分组

3
在过去的几年中,我参与了一些项目,我们在对象层次结构中遇到了一个类似的问题,这似乎总是会导致问题。我想知道是否有任何经典的OOP(Java、C#、PHP5等)设计模式可以优雅地处理这种情况。
假设我们有一个现有系统。该系统具有两种类型的实体,每个实体都用一个单独的类建模。比方说:
1. 客户 2. 销售代表
由于历史原因,这些类都没有继承相同的基类或共享通用接口。
我看到的问题是,不可避免地,新功能被规定为需要将客户和销售代表视为同一类型的对象。过去处理这个问题的方式是创建一个包含两者成员变量的新类,然后每个方法将根据设置的对象以不同的方式操作。
//pseudo PHPish code
class Participator
{
    public $customer;
    public $salesRepresentative;

    public function __construct($object)
    {
        if(object is instance of Customer)
        {
            $this->customer = $object;
        }

        if(object is instance of SalesRepresentative)
        {
            $this->salesRepresentative = $object;
        }           
    }

    public function doesSomething()
    {

        if($customer)
        {
            //We're a customer, do customer specific stuff
        }
        else if($salesRepresentative)
        {
            //We're a salesRepresentative, do sales 
            //representative specific stuff
        }           
    }
}

有没有更优雅的方式处理这种情况?
3个回答

9
也许可以在这里使用一个包装器。创建一个名为ParticipatorWrapper的包装器接口,指定新功能并为每个类构建具体的包装器,例如CustomerWrapper和SalesRepresentativeWrapper,两者都实现了新功能。
然后只需将对象包装在适当的包装器中,并编写针对ParticipatorWrapper的代码。
更新:类似Java的代码:
interface ParticipatorWrapper{
    public void doSomething();
}

class CustomerWrapper implements ParticipatorWrapper{
    Customer customer;
    public void doSomething(){
       //do something with the customer
    }
}

class SaleREpresentativeWrapper implements ParticipatorWrapper{
    SaleRepresentative salesRepresentative;
    public void doSomething(){
       //do something with the salesRepresentative
    }

}

class ClientOfWrapper{
    public void mymethod(){
         ParticipatorWrapper p = new ParticipatorWrapper(new Customer());
         p.doSomething();
   }
}

1
+1 OP的建议类似,但比在每个方法中使用if/else块更易于阅读和维护。如果直到运行时才知道类型,可能会与静态工厂方法配对使用。 - David Berger
+1 - 但是你需要添加一个工厂来创建适当的ParticipatorWrapper,或者将new ParticipatorWrapper(new Customer())行更改为创建CustomerWrapper。 - Eric Petroelje

2
这是对Vincent的回答提供的一种替代方案,采用了相反的方法。正如我在下面所述,存在一些缺点,但您特定的问题可能会忽略这些缺点,并且在这些情况下,我认为此解决方案更简单(或者您可能需要使用此解决方案和Vincent的某些组合)。
与其包装类,不如在类中引入钩子,然后将函数传递给它们。如果您希望从两个类中使用相同的数据执行相同的操作(根据您的抱怨,我猜测您是这样做的),那么这是一个合理的替代方案,因为这两个类没有共享的超类。
这里使用Visitor而不是Wrapper。Java中可以这样表示:
public <Output> Output visit(Vistor<Output> v) {
return v.process(...all shared the fields in Customer/SalesRep...);
}

然后你有一个访问者接口,所有函数都从中继承,它看起来像这样:
interface Visitor<Output> {
public Output process(...shared fields...);
}

有一些方法可以对传递给您的访问者进行切割,但这需要引入新的类来指定要使用的输入,这实际上变成了包装,因此您可能会选择使用Vincent的答案。

这种解决方案的缺点是,如果您执行某些更改类字段结构的操作,您可能会需要大量重构,而在Vincent的答案中这不是太大问题。如果您对存储在客户/销售代表实例中的数据进行修改,那么这种解决方案也会稍微不太有用,因为您实际上必须将其包装在访问者内部。


1
哦,这也假定您可以修改传统类。我假设您不是因为存在其他依赖关系而进行重构,而不是因为不允许这样做,否则我的解决方案将被排除在外。 - Carl
+1 非常有用的信息,因为我主要想看看哪些设计模式可以解决这个问题。当你生活在 PHP 的世界里时,你不会接触到很多真实世界的例子。 - Alana Storm

1

我认为你可以将mixins的概念应用到你的类中,以获得所需的功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接