Шаблон проектирования «набор определений» - известный под другим именем? - PullRequest
3 голосов
/ 31 декабря 2008

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

Основная идея заключается в создании объекта-посредника, который управляет набором объектов определения, причем каждый объект определения представляет собой возможное значение некоторого сложного свойства. Например, у вас могут быть классы Car, Plane и Generator, которые имеют EngineType. Автомобиль не хранит свой собственный объект EngineType, он хранит некоторый ссылочный ключ, в котором указывается тип двигателя (например, целое число или идентификатор строки). Когда мы хотим посмотреть на свойства или поведение EngineType, скажем, WankelEngine, мы запрашиваем одноэлементный объект EngineTypeBroker для определения объекта WankelEngine, передавая ему ссылочный ключ. Этот объект инкапсулирует все, что интересно знать о EngineTypes, возможно, просто является списком свойств, но потенциально может также загружаться в него.

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

Некоторые элементы этого шаблона, как я его использую (продолжаю использовать EngineType в качестве примера):

  1. Всегда есть функции IsEngineType (x) и EngineType (x) для определения, является ли заданное значение действительным ссылочным ключом для EngineType, и для извлечения объекта определения EngineType, соответствующего ссылочному ключу, соответственно.
  2. Я всегда разрешаю несколько форм ссылочного ключа для данного EngineType, всегда по крайней мере имя строки и самого объекта определения, чаще всего не целочисленный идентификатор, а иногда и типы объектов, которые объединяют EngineType. Это помогает в отладке, делает код более гибким и, в моей конкретной ситуации, облегчает множество проблем обратной совместимости по сравнению со старыми практиками. (Обычный способ, которым люди делали все это в контексте этого проекта, заключался в определении хэшей для каждого свойства, которое может иметь EngineType, и поиске свойств по ссылочному ключу.)
  3. Обычно каждый экземпляр определения является подклассом общего класса для этого типа определения (т. Е. WankelEngine наследует EngineType). Файлы классов для объектов определений хранятся в каталоге, таком как / Def / EngineType (то есть класс WankelEngine будет / Def / EngineType / WankelEngine). Таким образом, связанные определения группируются вместе, а файлы классов напоминают файлы конфигурации для EngineType, но с возможностью определения кода (обычно не встречается в файлах конфигурации).

Какой-то тривиально иллюстративный пример псевдокода:

class Car {

    attribute Name;
    attribute EngineTypeCode;

    object GetEngineTypeDef() {
        return EngineTypeBroker->EngineType(this->GetEngineTypeCode());
    }

    string GetDescription() {
        object def = this->GetEngineTypeDef();
        return "I am a car called " . this->GetName() . ", whose " .
            def->GetEngineTypeName() . " engine can run at " .
            def->GetEngineTypeMaxRPM() . " RPM!";
    }

}

Итак, есть имя для этого?

Ответы [ 6 ]

2 голосов
/ 31 декабря 2008

Я использовал шаблон, подобный этому ранее, чаще всего в играх. У меня были бы классы WeaponDefinition и WeaponInstance (не совсем с этими именами). Класс WeaponDefinition (и различные подклассы, если у меня есть разные типы оружия, например, ближний и дальний бой) будет отвечать за отслеживание глобальных данных для этого типа оружия (скорострельность, максимальные боеприпасы, имя и т. Д.) И имеет вся логика. Класс WeaponInstance (и его подклассы) содержит текущее состояние в последовательности стрельбы (для использования со сравнением скорострельности), текущее количество боеприпасов и указатель (это может быть некоторый ключ в классе менеджера, как в вашем примере, но это, кажется, не является обязательным требованием шаблона) для определения оружия. WeaponInstance имеет множество функций для запуска, перезагрузки и т. Д., Которые просто вызывают соответствующий метод в экземпляре WeaponDefinition, передавая себя в качестве аргумента. Это означает, что материал WeaponDefinition не дублируется для каждого танка / солдата / самолета в игровом мире, но у них у всех есть свое количество боеприпасов и т.д.

Я понятия не имею, как это называется, и я не уверен, что это то же самое, о чем вы говорите, но я думаю, что это близко. Это определенно полезно.

2 голосов
/ 31 декабря 2008

SingletonRegistry

Верьте мне или нет. Я думал о том же самом сегодня утром.

Я использовал этот шаблон раньше, но я никогда не нашел ссылку на него и не знаю, как его назвать.

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

В последний раз я использовал его для получения данных из разных источников.

У меня было около 50 таблиц базы данных (сделайте это 10) И у меня есть внешняя «таблица», в которой должны были отображаться данные, но данные могли поступать из любого из этих источников, и для каждой из них требовалась различная логика (запросы, соединения, ключи и т. д.)

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

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

Это было установлено в хэш-карте в начале и позже получено из таблицы базы данных.

Код был такой:

class DataFetcher {
    abstract Object getData( Object id );
}

class CustomerNameDataFetcher extends DataFetcher {
    Object getData( Object customerId ) { 
        // select name from customer where id = ? 
     }
}

class CompanyAdressDataFetcher extends DataFetcher { 
     Object getData( Object customerId ) { // don't ask why.
          // select name from company , customer where customer.co = company.co and cu = ?  etc.
     }
} 

class ProductColor extends DataFetcher { 
     Object getData( Object x ) { 
     // join from customer to color, to company to season to a bunch of table where id = ? 
}

// And the list goes on.

Каждый подкласс использует различную логику.

Во время выполнения пользователь настраивает свой вид и выбирает то, что он хочет видеть.

Когда пользователь выбрал столбцы для просмотра, я использовал имя столбца и идентификатор для извлечения данных.

DataFetchers были все установлены в родительском классе (я не хотел иметь отдельный класс для этого) в методе класса.

class DataFetcher {
    abstract Object getData( Object id );

    private static final Map fetchers = new HashMap();static { 
        fetchers.put("customer.name", new CustomerNameDataFetcher() );
        fetchers.put("company.address", new CompanyAdressDataFetcher () );
        fetchers.put("product.color", new ProductColor () );
        ...
    }
    public static DataFetcher getFetcher( String id ) { 
        return fetchers.get( id );
    }      

}

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

псевдокод

 for each row in table 
      for each column in row
          column.text = DataFetcher.getFetcher( column.id ).getData( row.id )
       end
 end

Это так? Или я неправильно понял ваше описание, и мое совсем другое.

Наконец, я думаю, это называется SingletonRegistry или что-то в этом роде. Я (вероятно), как вы, создал это по необходимости. Скорее всего, это общая картина.

1 голос
/ 07 мая 2014

Это немного похоже на сервисный локатор, в котором вес вашего пера зарегистрирован как синглтоны.

1 голос
/ 31 декабря 2008

Звучит как разновидность Flyweight (многие автомобили используют WankelEngine). Но как это имеет смысл? У большинства автомобилей есть двигатель, но как у многих из них может быть один и тот же экземпляр двигателя? Они бы так далеко не ушли. Или вы имеете в виду, что многие автомобили имеют двигатель типа WankelEngine? Spose, что имеет больше смысла. Тогда какая польза от "объекта определения WankelEngine"? Это Фабрика, которая строит ароматы этого объекта и передает их обратно заказчику? Если это так, то это не похоже на объект определения, звучит больше как Фабрика, которая принимает параметры объекта для создания и возвращает этот объект.

Здесь я вижу некоторые хорошие практики GoF, в частности, которые вы пишете, а не наследуете (у моей машины есть двигатель против двигателя моей машины - это WankelEngine). Хотелось бы, чтобы я мог точно вспомнить цитату, но это что-то вроде «инкапсуляция наследственных разрывов» и «композиция предпочтений перед наследованием».

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

Ребята из GoF обсуждают составление шаблонов в более крупные шаблоны, в частности MVC представляет собой совокупность трех других шаблонов. Похоже, вы сделали что-то подобное.

1 голос
/ 31 декабря 2008

Звучит как комбинация GoF Builder, Prototype и, возможно, полулегкого веса для меня.

0 голосов
/ 12 декабря 2011

Что у вас есть карта, он же словарь. У вас есть уникальный поворот, когда несколько ключей отображаются на один объект.

Почему это карта:

  • держи ключ
  • Используйте ключ для запроса значения из некоторой структуры данных.

Вы можете найти реализации в:

  • STL (включает: карта)
  • Java (импорт: java.util.Dictionary)
...