Делаю магазин по продаже цифровых товаров, на java, столкнулся с проблемой
выделения сущностей.
поля, которые должен иметь каждый товар:
abstract Ware {
name
rating
amountPurchases
createDate
price
}
дальше товар, которым предстоит торговать, с дополнительными полями:
Фильм img, about, [size, file] 1
Книга img, about, [size, file] 1
Программа img, about, [size, file] 1
Альбом img, about 2
Активационный номер img, about 2
Композиция(mp3) [size, file] 3
Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более
интересное решение?
Здравствуйте, Foror, Вы писали:
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Если дополнительных полей не так уж много, сделайте один класс Товар и включите в него все поля, которые нужны. Для отдельных товаров, где данные поля не нужны, их можно держать пустыми или инициализировать специальными значениями.
Не знаю как на этом форуме соообщения править. Поэтому так дополню (не внимательно глянул первое сообщение).
1 сущность — абстрактный класс товара, который описывает стандартные его свойства. А последующие описывают товары (один класс — один тип товара). Делать как написал предыдующий оратор, я бы точно не стал. Потом эти if() включать...
Преимущества:
[li] Самый простой и понятный вариант;
[li] Очень легко добавить новый товар;
[li] Очень легко добавить общую для всех товаров характеристику .
Здравствуйте, Foror, Вы писали:
F>Делаю магазин по продаже цифровых товаров, на java, столкнулся с проблемой F>выделения сущностей.
F>поля, которые должен иметь каждый товар:
F>abstract Ware { F>name F>rating F>amountPurchases F>createDate F>price F>}
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Вот у меня вопрос. Пользователь магазина сам сможет добавлять новые виды товаров, или каждый раз должен будет напрягать тебя для этого?
По мне, так товар — клас не абстрактный, а конкретный. Только помимо всего прочего должен содержать в себе ссылку на описание его типа. Описание типа — это имя с описанием набора допустимых аттрибутов. А конкретный экземпляр товара — это один инстанс класса Ware и набор значений аттрибутов, соотвествующих его типу.
Здравствуйте, LeonidV, Вы писали:
LV>1 сущность — абстрактный класс товара, который описывает стандартные его свойства. А последующие описывают товары (один класс — один тип товара). Делать как написал предыдующий оратор, я бы точно не стал. Потом эти if() включать...
Вы, видимо, не прочитали статью. Иначе бы поняли, что if'ы замучают, если сделать так, как написали Вы.
Здравствуйте, Кирилл Лебедев, Вы писали:
>Если дополнительных полей не так уж много, сделайте один класс Товар и включите в него все поля, которые нужны. Для >отдельных товаров, где данные поля не нужны, их можно держать пустыми или инициализировать специальными значениями.
Статью прочитал (кстати, спасибо за ссылочку), про if'ы не понял. Три раза пытался ответ написать, а потом понял — говорить так, не зная полной архитетуры, очень сложно. Я вообще делаю очень похоже на то, как Lucker написал. У меня задача такая — сделать магазин с незабитым ассортиментом.
Здравствуйте, Lucker, Вы писали:
L>Вот у меня вопрос. Пользователь магазина сам сможет добавлять новые виды товаров, или каждый раз должен будет напрягать тебя для этого? L>По мне, так товар — клас не абстрактный, а конкретный. Только помимо всего прочего должен содержать в себе ссылку на описание его типа. Описание типа — это имя с описанием набора допустимых аттрибутов. А конкретный экземпляр товара — это один инстанс класса Ware и набор значений аттрибутов, соотвествующих его типу.
Тут все зависит не от статики, а от поведения класса предка, и окружения.
Если класс товар является:
1. Неотъемлемым свойством остальных товаров
2. Его поведение независит от того, от кого его наследует
то вполне можно и реально создавать именно абстрактного предка. Это может положительно сыграть на простоте реализации. Если есть надежда что хоть для одного товара данные правила не будут выполнятся, то лучше композиция. Типы можно и в runtime генерить.
Здравствуйте, LeonidV, Вы писали:
LV>Статью прочитал (кстати, спасибо за ссылочку), про if'ы не понял. Три раза пытался ответ написать, а потом понял — говорить так, не зная полной архитетуры, очень сложно.
Постараюсь пояснить. Ответ будет содержать несколько пунктов:
1. Обычно данные группируют в класс для того, чтобы упростить работу с ними. Проще работать с классом целиком, чем с каждым полем класса, будь оно представлено в виде самостоятельной переменной.
В нашем случае мы группируем различные атрибуты товара в класс Товар для того, чтобы достигнуть той же цели. С товаром – как с целым – работать проще.
2. Из п. 1 следует, что группировка делается ради пользователя класса. Иными словами, создание класса оправдано в том и только том случае, если это упрощает пользовательский код. Если мы создали группу (например, класс), из-за которой вызывающий код усложнился, то, вероятнее всего, произведенная группировка ошибочна. И от нее надо отказаться. Или следует ее переделать.
3. Заменяя один класс на иерархию, мы тем самым разбиваем группу на куски. Был один класс (Товар), а стало несколько: Базовый_Товар, Товар_с_Картинкой, Товар_с_Мелодией, Товар_с_Видео_Клипом и т.д.
Разбивая группу на части, мы устраняем преимущество, созданное группировкой. Посудите сами, если раньше мы могли работать с Товаром, как с единым целым, то теперь нам нужно различать, с каким товаром мы имеем дело. Фактически, замена класса на иерархию обуславливает появление оператора if в пользовательском коде:
if (typeid(*pObj) == typeid(Базовый_Товар))
{
// делаем одно
}
else if (typeid(*pObj) == typeid(Товар_с_Картинкой))
{
// делаем другое
}
else if (typeid(*pObj) == typeid(Товар_с_Мелодией))
{
// делаем третье
}
else if (typeid(*pObj) == typeid(Товар_с_Видео_Клипом))
{
// делаем четвертое
}
Такое ветвление придется написать, например, в коде отображения товара на витрине.
4. В некоторых – весьма простых! – случаях ветвление можно заменить полиморфизмом. Для этого код, следующий за оператором if, нужно поместить в виртуальную функцию соответствующего класса:
class Товар
{
public:
virtual void DrawOnPage(HtmPage & rPage);
};
class Товар_с_Картинкой : public Товар
{
public:
virtual void DrawOnPage(HtmPage & rPage);
};
class Товар_с_Мелодией : public Товар
{
public:
virtual void DrawOnPage(HtmPage & rPage);
};
class Товар_с_Видео_Клипом : public Товар
{
public:
virtual void DrawOnPage(HtmPage & rPage);
};
Такое решение действительно устраняет if, но оно неработоспособно, если требуется создать несколько вариантов оформления html-страницы. При добавлении нового варианта (стиля оформления сайта) нужно будет добавлять новую функцию DrawOnPage во все классы иерархии. Причина этой проблемы в том, что мы привязали бизнес-логику (класс Товар) к представлению (класс HtmlPage). А сделали мы это только ради того, чтобы устранить if.
Оператор if появился только после преобразования класса Товар в иерархию. Соответственно, рассуждая логически, можно найти первопричину проблем – разбиение класса Товар на группу классов и выделение суперкласса. (Ситуация, аналогичная задаче с датчиками из статьи.)
5. Отсюда вывод: Лучше добавить лишних полей, но сохранить один класс, чем преобразовать класс в иерархию классов, выделяя в суперкласс общие поля.
Иными словами на группу (класс) лучше смотреть с точки зрения кода, который будет ее использовать, чем с точки зрения отдельных элементов этой группы.
Здравствуйте, Foror, Вы писали:
F>Делаю магазин по продаже цифровых товаров, на java, столкнулся с проблемой F>выделения сущностей.
F>поля, которые должен иметь каждый товар:
F>abstract Ware { F>name F>rating F>amountPurchases F>createDate F>price F>}
F>дальше товар, которым предстоит торговать, с дополнительными полями:
F>Фильм img, about, [size, file] 1 F>Книга img, about, [size, file] 1 F>Программа img, about, [size, file] 1 F>Альбом img, about 2 F>Активационный номер img, about 2 F>Композиция(mp3) [size, file] 3
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Я бы сделал так: База данных:
tableWaretableProperties
ID........................ware_ID
name prop_name
rating prop_value
amountPurchases
createDate
price
В tableProperties хранишь все пары (имя — значение) дополнительных свойств.
Код:
Создаешь один единственный суперкласс Ware и все дополнительные свойства из tableProperties загоняешь в ArrayList. Ну а дальше дело техники, как тебе эти значения получать. Пишешь, например метод возвращающий список всех доступных дополнительных свойств. Другой метод, который тебе по имени свойства возвращает его значение.
И все. добавляй себе сколько угодно нового товара с новыми дополнительными свойствами, все будет работать.
Если добро всегда побеждает зло, значит кто победил — тот и добрый.
Классы: Продукт, Катерория, Свойство товара, Значение свойства товара
У категории — коллекция свойств товара
У товара — коллекция значений свойств
У свойства товара — ссылка на категорию
У значения свойства — ссылка на свойство товара
Или вы хотите чтобы клиент сам дописывал нужные классы если он решил внести в ассортимент новый тип товаров? Это прекрасный способ привязать к себе клиента, но до поры до времени.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>5. Отсюда вывод: Лучше добавить лишних полей, но сохранить один класс, чем преобразовать класс в иерархию классов, выделяя в суперкласс общие поля.
и получить швейцарский армейский нож?
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>3. Заменяя один класс на иерархию, мы тем самым разбиваем группу на куски. Был один класс (Товар), а стало несколько: Базовый_Товар, Товар_с_Картинкой, Товар_с_Мелодией, Товар_с_Видео_Клипом и т.д.
Неверно. Мы оставляем исходную группу неизменной. Наследники только образуют новые группы.
КЛ>Разбивая группу на части, мы устраняем преимущество, созданное группировкой. Посудите сами, если раньше мы могли работать с Товаром, как с единым целым, то теперь нам нужно различать, с каким товаром мы имеем дело. Фактически, замена класса на иерархию обуславливает появление оператора if в пользовательском коде:
[...] КЛ>Такое ветвление придется написать, например, в коде отображения товара на витрине.
Посылка правильная, выводы неверные. if-ы появятся только при глупости или лени проектировщика.
КЛ>4. В некоторых – весьма простых! – случаях ветвление можно заменить полиморфизмом. Для этого код, следующий за оператором if, нужно поместить в виртуальную функцию соответствующего класса:
[...] КЛ>Такое решение действительно устраняет if, но оно неработоспособно, если требуется создать несколько вариантов оформления html-страницы. При добавлении нового варианта (стиля оформления сайта) нужно будет добавлять новую функцию DrawOnPage во все классы иерархии. Причина этой проблемы в том, что мы привязали бизнес-логику (класс Товар) к представлению (класс HtmlPage). А сделали мы это только ради того, чтобы устранить if.
Правильно сделали. И дальше нужно идти по тому же пути — углублять структуризацию, а не шарахаться от неё путём внесения аналитических if на верхний уровень.
КЛ>Оператор if появился только после преобразования класса Товар в иерархию. Соответственно, рассуждая логически, можно найти первопричину проблем – разбиение класса Товар на группу классов и выделение суперкласса. (Ситуация, аналогичная задаче с датчиками из статьи.)
Неправда ваша. Операторы if(тип) появляются при низкой квалификации проектировщика. Проверено неоднократно.
КЛ>5. Отсюда вывод: Лучше добавить лишних полей, но сохранить один класс, чем преобразовать класс в иерархию классов, выделяя в суперкласс общие поля.
Не согласен. Как минимум, остаётся ещё возможность агрегации, да и с наследованием не всё так фатально, как кажется на первый взгляд. Есть, как минимум ещё один выход: сделать фабрику для рисовальщиков. Фабрика будет генерировать классы в зависимости от контекста (т.е., например — способа оформления).
public interface IDrawer
{
public void draw(HtmlPage page)
{ /*...*/ }
}
public class Ware implements IWare
{
public IDrawer create_drawer(int drawing_mode)
{
// WareDrawer - фабрика, создающая рисовальщиков для класса Ware
// в зависимости от значния параметра.return SomeWareDrawerFactory.create_new_drawer(this, drawing_mode);
}
}
// Пример реализации WareDrawerFactorypublic class SomeWareDrawerFactory
{
// Внимание: обратите внимание на тип параметра to_draw!public static IDrawer create_new_drawer(Ware to_draw, int drawing_mode)
{
if (drawing_mode == 1) return new DrawerForMode1(to_draw);
if (drawing_mode == 2) return new DrawerForMode2(to_draw);
return null;
}
}
Наследники Ware перекрывают create_drawer обращением к "своим" фабрикам. Результирующий "рисовальщик" есть производное от выбора по типу товара и способу отображения.
Да, естественно, в этом случае получится n*m типов рисовальщтков, где n — количество типов товара, а m — количество "типов оформления". Но по сути тоже самое мы получаем и в любом случае, коль начинаем беседу о "типах" товара и отображения.
КЛ>Иными словами на группу (класс) лучше смотреть с точки зрения кода, который будет ее использовать, чем с точки зрения отдельных элементов этой группы.
+1
И использование получится очень простым и полиморфным:
public void DrawOnPage(IWare ware, HtmlPage page, int draw_mode)
{
IDrawer drawer = ware.create_drawer(draw_mode);
drawer.draw(page);
}
Можно ещё поиграться с замыканиями ware и drawer, подумать о предварительном создании набора рисовальщиков, возможно, о введении зависимости от типа страницы и т.д. и т.п.
Кстати, только в фабриках анализ типа объекта может быть уместен, да и то лишь в том случае, если требуется привязать тип создаваемого объекта к типу объекта-аргумента.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
public interface IDrawer
{
public void draw(HtmlPage page)
{ /*...*/ }
}
а просто:
public interface IDrawer
{
public void draw(HtmlPage page);
}
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Если вы посмотрите на исходный текст, то там последняя фраза следующая:
Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более
интересное решение?
Так что как поизвращаться с наследниками у автора проблемы не возникает. Наоборот, он старается от этого избавиться. И правильно делает. Из условия задачи выходит что 3 наследника причиняют больше неудобств чем выгоды.
По моему глубокому IMHO, создавать наследников следует только если от этого есть какая-то практическая польза, а не тупо передирать патерны из учебника, чтобы получилось красиво.
В данном конкретном случае можно обойтись одним единственным класом, только если автор не объяснит зачем ему нужны наследники. Пока это не ясно.
Если добро всегда побеждает зло, значит кто победил — тот и добрый.
Re: Цифровой товар, выделить суперклассы
От:
Аноним
Дата:
13.04.06 06:07
Оценка:
Здравствуйте, Foror, Вы писали:
... F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Не то, что б совсем уж имхо...
Самое важное: любое, принятое тобой решение, должно предполагать единообразную обработку различных товаров (реализуют один интерфейс).
В остальном я бы предложил воспользоваться таким правилом: потомок должен создаваться, если у него будут уникальные поля и методы. Например, у фильма: trailer и ShowTrailer(). Если нет — в товар добавь тип товара.
Еще один нюанс. Подумай в каком направлении будет происходить развитие. Возможно, лучше сразу заложить в арихитектуру будущие изменения.
Здравствуйте, UnIc0dE, Вы писали:
UIE>Так что как поизвращаться с наследниками у автора проблемы не возникает. Наоборот, он старается от этого избавиться. И правильно делает. Из условия задачи выходит что 3 наследника причиняют больше неудобств чем выгоды.
Из контекста вообще ничего не видно. Пользуясь только информацией о данных, можно только сделать выводы как это может лежать на БД. И то только в классической нормализованной модели. Обычно приходится держать в голове наиболее криминальные транзакции. Делать же выводы по дизайну ООП можно только основываясь на поведении(о чем совершенно не знает г-п Лебедев). Данные в данном случае — вторичны.
Здравствуйте, UnIc0dE, Вы писали:
UIE>По моему глубокому IMHO, создавать наследников следует только если от этого есть какая-то практическая польза, а не тупо передирать патерны из учебника, чтобы получилось красиво.
Полностью согласен с Вашей точкой зрения. Не смотря на то, что данные не важны (и это правильно отметил Глеб Земсков), важно то, как они сгруппированы. Группировка же должна делаться, исходя из удобств использования, а не из того, как эти данные "лежат" в базе данных. Чего, к сожалению, не понимает (и не хочет понять!) Глеб Земсков. Иными словами, пользователь первичен, а база данных — вторична. Проблема оптимизации количества запросов к базе данных хотя и важна, но тоже вторична. И решается она совсем на другом системном уровне.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Группировка же должна делаться, исходя из удобств использования,
Ну вот, хоть что-то. КЛ>а не из того, как эти данные "лежат" в базе данных. Чего, к сожалению, не понимает (и не хочет понять!) Глеб Земсков.
И где это ты слышал? О независимости модели данных и объектной модели слышал? КЛ>Иными словами, пользователь первичен, а база данных — вторична.
Интересная постановка задачи. Никогда об этом не задумывался.
Здравствуйте, Foror, Вы писали:
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Я буду делать две сущности + одна абстрактная. Первая сущность может
иметь своё изображение, и товар, который наследует эту сущность, должен выводиться пользователю
с учетом этих данных. Вторая сущность имеет файл и изображение, а значит для товара данного
типа будет свой вывод. От третьей сущности(которая имеет только файл) отказался, т.к. только
одна Музыкальная композиция имеет случай с файлом без изображения. Этот частный случай наследует
напрямую от абстрактного товара.
А делать динамические поля пока не нужно, и вообще наврятли я думаю понадобятся от специфики
продаваемого товара.