2009-09-16 4 views
2

За последние несколько лет я занимался проектами, в которых мы столкнулись с подобной проблемой в нашей иерархии объектов, которая всегда вызывает проблемы. Мне было любопытно, знал ли кто-нибудь о классическом шаблоне дизайна OOP (Java, C#, PHP5 и т. Д.), Который мог бы изящно справиться с этой ситуацией.Дизайн-шаблон для группировки похожих объектов вместе

Скажем, у нас есть существующая система. Эта система имеет, помимо прочего, два типа объектов, каждый из которых имеет индивидуальный класс. Скажем

  1. Заказчик

  2. SalesRepresentative

По историческим причинам, ни один из этих классов наследуют от базового класса или имеют общий интерфейс.

Проблема, которую я видела, неизбежно, что новая функция выясняется, что требует от нас рассмотрения Клиента и SalesRepresentative как одного и того же типа Объекта. То, как я видел это обработано в прошлом, чтобы создать новый класс, который включает в себя переменную-член для обоих, а затем каждый метод будет работать на объекты по-разному в зависимости от того, который установлен

//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 
     }   
    } 
} 

Есть более грациозный способ справиться с такой ситуацией?

ответ

8

Возможно, здесь может использоваться обертка. Создайте интерфейс Wrapper, скажем, UserportWrapper, который определяет новые функции и создает конкретные Wrappers для каждого класса, например ClientWrapper и SalesRepresentativeWrapper, которые реализуют новые функции.

Затем просто оберните объект в соответствующую оболочку и напишите код, предназначенный для участника-участника.

Update: Javaish Код:

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: Хороший пример шаблона адаптера. – Adamski

+0

+1 Аналогичная логика для предложения OP, но гораздо легче читать и поддерживать, чем if/else block в каждом методе. Возможно, пара со статическим заводским методом, если вы не знаете тип до выполнения. –

+0

+1 - но вам нужно будет либо добавить фабрику для создания соответствующего участника-участника, либо изменить новую строку «Заказчик» (новый клиент()), чтобы вместо этого создать ClientWrapper. –

1

Я думаю, вы могли бы применить концепцию mixins к классам, чтобы получить функциональность вы хотите.

2

Это альтернатива тому ответу Винсента, который использует противоположный подход. Как я заметил ниже, есть некоторые недостатки, но ваша конкретная проблема может устранить их, и я думаю, что это решение проще в этих случаях (или вы можете использовать некоторую комбинацию этого решения и Vincent's).

Вместо того, чтобы обертывать классы, вводите крючки в классы, а затем передавайте им функции. Это разумная альтернатива, если вы хотите сделать то же самое с одними и теми же данными из обоих классов (что, как я полагаю, вы полагаете, на основании сожаления, что у двух классов нет общего суперкласса).

Используется Visitor вместо Wrapper.Javaish это будет что-то вроде:

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...); 
} 

Есть как-нибудь, чтобы расколоть, что переходит в руки вашего посетителя, но вовлекает в себя введение новых классов, чтобы указать, какие входы использовать, что в любом случае становится обертыванием, поэтому вы можете использовать ответ Винсента.

Недостатком этого решения является то, что вы делаете что-то, что изменяет структуру полей класса, вы можете купить себе много рефакторинга, что является проблемой не только в ответе Винсента. Это решение также немного менее полезно, если вы вносите изменения в данные, хранящиеся в экземпляре Customer/SalesRep, так как вам действительно нужно обернуть их внутри Visitor.

+1

О, это также предполагает, что вы можете вносить изменения в устаревшие классы. Я предполагаю, что вы не рефакторинг из-за других существующих зависимостей, а не потому, что вам не разрешено это делать, что исключает мое решение. – Carl

+0

+1 для хорошей информации, так как меня в основном интересовали различные шаблоны дизайна, которые МОГУТ решить проблему. Вы не можете столкнуться с множеством реальных примеров, когда вы живете на земле PHP. –

Смежные вопросы