Дизайн шаблона вопрос - PullRequest
       11

Дизайн шаблона вопрос

7 голосов
/ 17 декабря 2009

Я новичок в шаблонах дизайна, и вот мой вопрос

если у нас есть абстрактный класс с несколькими классами, которые его реализуют, и каждый из этих классов имеет разные атрибуты.

теперь у меня есть другой (класс Manager), который содержит массив абстрактного класса, и я хочу добавить в него метод поиска ... как я могу это сделать, не приводя к конкретным классам?

У меня есть 2 идеи:

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

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

солютин таков: каждый добавленный новый элемент должен будет переопределять один интерфейс, который заставляет его генерировать полное описание его полей, например, объект автомобиля должен будет возвращать хеш-карта, имеющая следующий

field: {fieldType, fieldValue}

пример

  • модель: {текст, "ford"}
  • manifactureDate: {Дата, "12/1/89"}

и каждый объект должен будет также реализовать метод под названием CompareFields, который принимает Вот такую ​​хэш-карту, сравните ее с ее полем и верните true или false.

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

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

Я до сих пор не понимаю, как я буду обрабатывать сложный объект (у которого есть другой объект в качестве его атрибутов)

Я не знаю, что это за шаблон ... это просто идея, о которой я думал.

РЕДАКТИРОВАТЬ: БЕТОННЫЙ ПРИМЕР

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

Ответы [ 8 ]

9 голосов
/ 17 декабря 2009

Инкапсуляция

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

interface IVehicle{
   bool doesMatch( Map<String,String> searchCriterion )
}

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

Посетитель

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

class VehicleSearchVisitor
{
   Map<String,String> searchCriterion;
   void visit( Car car ) {...}
   void visit( Bike bike ) { ... }
   ....
}

Мета-программирование

Идея объекта, который самоописывает себя, является другой концепцией, которая называется метапрограммированием. Затем уровень представления introspect другого объекта, чтобы знать, как обрабатывать их. Это традиционно считается продвинутой ОО-техникой. Вы можете создать собственные аннотации для описания поля вашего класса, чтобы уровень представления мог динамически отображать соответствующую метку. Та же идея, например, используется с аннотациями в спящем режиме. Мета-программирование нужно делать осторожно, иначе вы столкнетесь с другой проблемой.

Insteanceof

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

В общем, я бы порекомендовал использовать для поиска посетителя, а не использовать метапрограммирование для уровня презентации и вместо этого создавать одну простую страницу для каждого типа транспортного средства.

6 голосов
/ 17 декабря 2009

Хорошо, вы расширяете класс, а не реализуете интерфейс. Если бы у вас были классы Bus, Car, Truck, Train и все они реализовали IVehicle, для которого требовалась функция, возвращающая сортируемое / доступное для поиска значение, вы могли бы ссылаться на все из них как на тип IVehicle и вызывать этот метод на всех .

Код ActionScript 3:

package com.transportation.methods {
  public interface IVehicle {
    function getSpeed():Number;
    function getOtherSortableOrSearchableValue():*;
  }
}

и

public class Car extends Sprite implements IVehicle

Вы должны определить getSpeed ​​() и getOtherSortableValue () в классе Car и можете ссылаться на него как на Car или IVehicle. Поскольку все виды транспорта в моем примере будут реализовывать IVehicle, если вы ссылаетесь на них как на IVehicles, вы можете вызывать эти две функции.

4 голосов
/ 17 декабря 2009

Вы, кажется, описываете то, что Стив Йегге называет Универсальным шаблоном дизайна.

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

2 голосов
/ 17 декабря 2009

Это звучит как работа для шаблона посетителя. У вас есть коллекция абстрактных объектов, и вы не знаете, что каждый из них. Используя Visitor, вы можете перебирать все объекты и выполнять действия, специфичные для каждого из них. Книга GOF Patterns содержит подробные сведения о шаблоне Visitor, но я постараюсь привести хороший пример Java здесь.

public class Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public interface VehicleVisitor {
    public void visit( Vehicle vehicle );
    public void visit( Car car );
    public void visit( Bus bus );
    public void visit( Truck truck );
    // Augment this interface each time you add a new subclass.
}

public class Car extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Bus extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Truck extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class VehicleSearch implements VehicleVisitor {
    protected String name;
    public List<Vehicle> foundList =
        new ArrayList<Vehicle>();
    public VehicleSearch( String name ) {
        this.name = name;
    }
    public void visit( Vehicle vehicle ) {
        return;
    }
    public void visit( Car car ) {
        if ( car.getModel().contains( name ) ) {
            foundList.add( car );
        }
    }
    public void visit( Bus bus ) {
        if ( bus.getManufacturerModel().contains( name ) ) {
            foundList.add( bus );
        }
    }
    public void visit( Truck truck ) {
        if ( truck.getLineModel().contains( name ) ) {
            foundList.add( truck );
        }
    }
}

public class Manager {
    protected List<Vehicle> vehicleList;
    public List<Vehicle> search( String name ) {
        VehicleSearch visitor =
            new VehicleSearch( name );
        for ( Vehicle vehicle : vehicleList ) {
            vehicle.accept( visitor );
        }
        return visitor.foundList;
    }
}

На первый взгляд, да, вы можете просто добавить метод поиска в класс Vehicle и вызвать его для каждого члена списка, но шаблон посетителей позволяет вам определять несколько операций над списком транспортных средств, так что вы не необходимо добавить новый метод в класс Vehicle для каждого.

Однако одним из недостатков шаблона «Посетитель» является необходимость изменения самого посетителя при добавлении нового класса посещаемого объекта. В этом примере, если вы добавите автомобиль RocketShip в систему, вам нужно будет добавить метод visit(RocketShip rocketShip) для посетителя.

Вы можете устранить эту проблему, создав шаблон VehicleVisitorTemplate со всеми переопределенными методами посещения. Ваши операции подклассируют класс шаблона и затем нужно только переопределить необходимые методы. Когда вам нужно добавить новый класс и метод посещения, вы добавляете его в интерфейс и класс шаблона, а затем все другие операции не нуждаются в обновлении без необходимости.

2 голосов
/ 17 декабря 2009

Мне кажется, что это своего рода «централизованный» шаблон репозитория. Я не думаю, что это хорошая идея.

Что вы рекомендуете делать, так это то, что вместо одного централизованного способа общего поиска у вас должно быть несколько специализированных репозиториев: один для автомобилей, один для лодок, один для самолетов и т. Д.

Каждый из репозиториев знает о деталях соответствующих объектов. Затем вы можете выделить общие операции между этими специализированными репозиториями в базовый класс или интерфейс репозитория. Когда вас не волнуют детали, вы используете базовый интерфейс репозитория. Когда вы заботитесь о деталях, вы используете специализированный репозиторий.

Вот упрощенный пример на Java (для краткости я опускаю методы получения / установки - не делайте ваши поля открытыми):

interface Vehicle { int id; int maxSpeed; }
class Car implements Vehicle { int doors; int id; int maxSpeed; }
class Boat implements Vehicle { int buoyancy; int id; int maxSpeed; }
class Plane implements Vehicle { int wingSpan; int id; int maxSpeed; }

interface VehicleRepository<T extends Vehicle> {
    T getFastest();
    T getSlowest();
    T getById(int id);
}

interface CarRepository inherits VehicleRepository<Car> {
    List<Car> getCarsWithTwoDoors();
}

interface BoatRepository inherits VehicleRepository<Boat> {
    Boat getMostBuoyantBoat();
}

interface PlaneRepository inherits VehicleRepository<Plane> {
    List<Plane> getPlanesByWingspan();
}
0 голосов
/ 17 декабря 2009

Это звучит как сценарий использования для шаблона спецификации.

http://en.wikipedia.org/wiki/Specification_pattern

http://devlicio.us/blogs/casey/archive/2009/03/02/ddd-the-specification-pattern.aspx

Удачи!

0 голосов
/ 17 декабря 2009

как менеджер, например, менеджер трафика, может найти определенный элемент, используя абстрактный класс, без приведения в машину или автобус

Я бы сделал так, чтобы у абстрактного класса был абстрактный метод, который возвращает значение идентификатора (скажем, значение перечисления), который указывает, к какому конкретному типу относится объект, и каждый конкретный класс переопределяет это для возврата его значение ID.

Или я бы попытался использовать средства RTTI C ++ или функцию Python instanceof().

Помогает ли это, или я отвечаю не на тот вопрос?

0 голосов
/ 17 декабря 2009

Я не вижу, какой язык вы используете, но почему бы абстрактному классу не реализовать интерфейс? Таким образом, не имеет значения, сколько конкретных классов у вас есть, если они все наследуются от абстрактного класса, который реализует интерфейс.

Вот как будет выглядеть иерархия, если вы используете Java:

public interface IVehicle {/*your code goes here*/}

public abstract class AbstractVehicle implements IVehicle{/*your code goes here*/}

public class Car extends AbstractVehicle{/*your code goes here*/}

Все они, конечно, будут определены в разных файлах.

...