Добавление поля в класс во время выполнения - шаблон проектирования


15

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

Вместо того, чтобы иметь свойства как поля:

class Car extends Product {
   protected String type;
   protected int seats;
}

Вы, вероятно, в конечном итоге сделать что-то вроде:

class Product {
   protected String productName;
   protected Map<String, Property> properties;
}

class Property {
   protected String name;
   protected String value;
}

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

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

Спасибо за уделенное время :).



Не уверен, какой язык вы используете, но если это C #, вы можете использовать динамический тип, который в основном хранит KVP в словаре, что-то вроде того, что вы делаете с продуктами, и позволяет вам просто привязывать свойства, не добавляя их непосредственно в коллекция как коллекция. У вас не будет строгой печати, хотя. Я знаю, что вы просили шаблон дизайна, но я не думаю, что вам понадобится что-то сложное, чтобы использовать их. msdn.microsoft.com/en-us/magazine/gg598922.aspx
Тони

Тони: Я использую здесь Java, но считаю это псевдо-кудой :). Позволит ли C # сохранить этот динамический объект в базе данных? Я сомневаюсь в этом, поскольку база данных должна знать структуру данных заранее.
Филипп

Существует государство Desing Pattern а-ля Gang-оф-четыре, что делает объект появится , чтобы изменить его тип или класс во время выполнения. Другими альтернативами являются Шаблон проектирования наблюдателя или Шаблон проектирования прокси
Никос М.

1
Почему бы просто не использовать тип данных карты? В БД это может быть представлено как {id} + {id, key, value}, если вы не запрашиваете производительность.
Shadows In Rain

Ответы:


4

Поздравляем! Вы только что совершили кругосветное плавание на языке программирования / системе типов, прибыв на другой край света, откуда вы отправились. Вы только что приземлились на границе динамического языка / прототипа объекта земли!

Многие динамические языки (например, JavaScript, PHP, Python) позволяют расширять или изменять свойства объекта во время выполнения.

В крайнем случае это язык на основе прототипов, такой как Self или JavaScript. У них нет занятий, строго говоря. Вы можете делать вещи, которые выглядят как объектно-ориентированное программирование на основе классов, с наследованием, но правила значительно упрощены по сравнению с более четко определенными языками на основе классов, такими как Java и C #.

Языковые языки, такие как PHP и Python, живут посередине. У них есть регулярные идиоматические системы на основе классов. Но атрибуты объекта могут быть добавлены, изменены или удалены во время выполнения - хотя и с некоторыми ограничениями (например, «кроме встроенных типов»), которых вы не найдете в JavaScript.

Большим компромиссом для этого динамизма является производительность. Забудьте, насколько сильно или слабо типизирован язык, или насколько хорошо его можно скомпилировать в машинный код. Динамические объекты должны быть представлены в виде гибких карт / словарей, а не простых структур. Это добавляет накладные расходы на каждый доступ к объекту. Некоторые программы идут на все, чтобы уменьшить эти издержки (например, с помощью фантомного назначения kwarg и основанных на слотах классов в Python), но дополнительные накладные расходы обычно равны курсу и стоимости приема.

Возвращаясь к своему дизайну, вы прививаете возможность иметь динамические свойства в подмножестве ваших классов. ProductМожет иметь переменные атрибуты; по-видимому, Invoiceили Orderбы и не мог. Это не плохой путь. Это дает вам гибкость, позволяющую варьировать, где вам это необходимо, оставаясь при этом в строгой, дисциплинированной системе языков и типов. С другой стороны, вы несете ответственность за управление этими гибкими свойствами, и вам, вероятно, придется делать это с помощью механизмов, которые немного отличаются от более собственных атрибутов. p.prop('tensile_strength')а не p.tensile_strength, например, p.set_prop('tensile_strength', 104.4)а неp.tensile_strength = 104.4, Но я работал со многими программами на Pascal, Ada, C, Java и даже на динамических языках, которые использовали именно такой доступ для получения-установки для нестандартных типов атрибутов; подход явно работоспособен.

Между прочим, это напряжение между статическими типами и очень разнообразным миром чрезвычайно распространено. Аналогичная проблема часто наблюдается при разработке схемы базы данных, особенно для реляционных и предреляционных хранилищ данных. Иногда это делается путем создания «супер-строк», которые содержат достаточно гибкости, чтобы содержать или определять объединение всех воображаемых вариаций, а затем вводить любые данные, которые попадают в эти поля. WordPress wp_postsтаблица , например, имеет поле , такие как comment_count, ping_status, post_parentи post_date_gmtчто только интересно при некоторых обстоятельствах, и что на практике часто гаснуть. Другой подход - это очень запасной, нормализованный стол wp_options, похожий на вашPropertyучебный класс. Хотя это требует более явного управления, элементы в нем редко бывают пустыми. Объектно-ориентированные базы данных и базы данных (например, MongoDB) часто легче справляются с изменением параметров, поскольку они могут создавать и задавать атрибуты практически по желанию.


0

Мне нравится вопрос, мои два цента:

Ваши два подхода радикально отличаются:

  • Первый - ОО и строго типизированный, но не расширяемый.
  • Второй слабо типизирован (строка инкапсулирует что угодно)

В C ++ многие использовали бы std :: map of boost :: option для достижения сочетания обоих.

Отступление: обратите внимание, что некоторые языки, такие как C #, позволяют динамически создавать типы. Что может быть хорошим решением для общей проблемы добавления участников динамически. Однако «модификация / добавление» типов после компиляции приводит к повреждению самой системы типов и делает ваши «измененные» типы практически бесполезными (например, как бы вы получили доступ к таким добавленным свойствам, поскольку вы даже не знаете, что они существуют? Единственный разумный способ - быть систематическим отражением каждого объекта ... заканчивая чисто динамическим языком _ вы можете обратиться к ключевому слову "dynamic" .NET)


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

0

Создание типа во время выполнения звучит намного сложнее, чем просто создание слоя абстракции. Распространено создание абстракции для разделения систем.

Позвольте мне показать пример из моей практики. Московская биржа имеет торговое ядро ​​Plaza2 с API трейдера. Трейдеры пишут свои программы для работы с финансовыми данными. Проблема в том, что эти данные очень большие, сложные и подвержены изменениям. Это может измениться после того, как введен новый финансовый продукт или изменен круг клиринга. Характер будущих изменений не может быть предсказан. Буквально, это может меняться каждый день, и плохие программисты должны редактировать код и выпускать новую версию, а злые трейдеры должны пересматривать свои системы.

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

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

  • Больше кода для разбора и проверки (и больше времени вне курса). Все, что компилятор сделал для вас со статической типизацией, вы должны делать во время выполнения.
  • Больше документации сообщений, таблиц или других примитивов. Вся сложность идет от кода к какой-то схеме или стандарту. Вот пример вышеупомянутой схемы финансового ада: http://ftp.moex.com/pub/FORTS/Plaza2/p2gate_en.pdf (десятки страниц таблиц)

0

Является ли этот подход известным шаблоном дизайна?

В XML и HTML это будут атрибуты узла / элемента. Я также слышал, что они называются расширенными свойствами, парами имя / значение и параметрами.

Вы бы решили проблему по-другому?

Вот так я бы решил проблему, да.

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

В некотором смысле база данных будет похожа на Java. В песо-sql:

TABLE products
(
    product_name VARCHAR(50),
    product_id INTEGER AUTOINCREMENT
)

TABLE attributes
(
    product_id INTEGER,
    name VARCHAR(50),
    value VARCHAR(2000)
)

Это будет соответствовать Java

class Product {
   protected String productName;
   protected Map<String, String> properties;
}

Обратите внимание, что в классе Property нет необходимости, поскольку карта хранит имя в качестве ключа.

Вы бы предпочли добавить / изменить столбцы или использовать что-нибудь, как показано выше?

Я пробовал добавить / изменить столбец, и это был кошмар. Это может быть сделано, но вещи продолжали идти синхронно, и я никогда не заставлял это работать хорошо. Структура таблицы, которую я изложил выше, была гораздо более успешной. Если вам нужно сделать поиск и заказ, рассмотрите используя атрибуты таблицы для каждого типа данных ( date_attributes, currency_attributesи т.д.) или добавив некоторые свойства , как старые добрые столбцы базы данных в таблице продуктов. Отчеты часто гораздо проще писать в столбцах базы данных, чем в подстолях.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.