Здравствуйте, 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. Отсюда вывод: Лучше добавить лишних полей, но сохранить один класс, чем преобразовать класс в иерархию классов, выделяя в суперкласс общие поля.
Иными словами на группу (класс) лучше смотреть с точки зрения кода, который будет ее использовать, чем с точки зрения отдельных элементов этой группы.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>5. Отсюда вывод: Лучше добавить лишних полей, но сохранить один класс, чем преобразовать класс в иерархию классов, выделяя в суперкласс общие поля.
и получить швейцарский армейский нож?
Здравствуйте, Lucker, Вы писали:
L>Вот у меня вопрос. Пользователь магазина сам сможет добавлять новые виды товаров, или каждый раз должен будет напрягать тебя для этого? L>По мне, так товар — клас не абстрактный, а конкретный. Только помимо всего прочего должен содержать в себе ссылку на описание его типа. Описание типа — это имя с описанием набора допустимых аттрибутов. А конкретный экземпляр товара — это один инстанс класса Ware и набор значений аттрибутов, соотвествующих его типу.
Тут все зависит не от статики, а от поведения класса предка, и окружения.
Если класс товар является:
1. Неотъемлемым свойством остальных товаров
2. Его поведение независит от того, от кого его наследует
то вполне можно и реально создавать именно абстрактного предка. Это может положительно сыграть на простоте реализации. Если есть надежда что хоть для одного товара данные правила не будут выполнятся, то лучше композиция. Типы можно и в runtime генерить.
Если вы посмотрите на исходный текст, то там последняя фраза следующая:
Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более
интересное решение?
Так что как поизвращаться с наследниками у автора проблемы не возникает. Наоборот, он старается от этого избавиться. И правильно делает. Из условия задачи выходит что 3 наследника причиняют больше неудобств чем выгоды.
По моему глубокому IMHO, создавать наследников следует только если от этого есть какая-то практическая польза, а не тупо передирать патерны из учебника, чтобы получилось красиво.
В данном конкретном случае можно обойтись одним единственным класом, только если автор не объяснит зачем ему нужны наследники. Пока это не ясно.
Если добро всегда побеждает зло, значит кто победил — тот и добрый.
Здравствуйте, Foror, Вы писали:
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Если дополнительных полей не так уж много, сделайте один класс Товар и включите в него все поля, которые нужны. Для отдельных товаров, где данные поля не нужны, их можно держать пустыми или инициализировать специальными значениями.
Здравствуйте, 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'ы замучают, если сделать так, как написали Вы.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>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.: Винодельческие провинции — это есть рулез!
Делаю магазин по продаже цифровых товаров, на 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
Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более
интересное решение?
Не знаю как на этом форуме соообщения править. Поэтому так дополню (не внимательно глянул первое сообщение).
1 сущность — абстрактный класс товара, который описывает стандартные его свойства. А последующие описывают товары (один класс — один тип товара). Делать как написал предыдующий оратор, я бы точно не стал. Потом эти if() включать...
Преимущества:
[li] Самый простой и понятный вариант;
[li] Очень легко добавить новый товар;
[li] Очень легко добавить общую для всех товаров характеристику .
Здравствуйте, Кирилл Лебедев, Вы писали:
>Если дополнительных полей не так уж много, сделайте один класс Товар и включите в него все поля, которые нужны. Для >отдельных товаров, где данные поля не нужны, их можно держать пустыми или инициализировать специальными значениями.
Статью прочитал (кстати, спасибо за ссылочку), про if'ы не понял. Три раза пытался ответ написать, а потом понял — говорить так, не зная полной архитетуры, очень сложно. Я вообще делаю очень похоже на то, как Lucker написал. У меня задача такая — сделать магазин с незабитым ассортиментом.
Здравствуйте, 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. Ну а дальше дело техники, как тебе эти значения получать. Пишешь, например метод возвращающий список всех доступных дополнительных свойств. Другой метод, который тебе по имени свойства возвращает его значение.
И все. добавляй себе сколько угодно нового товара с новыми дополнительными свойствами, все будет работать.
Если добро всегда побеждает зло, значит кто победил — тот и добрый.
Классы: Продукт, Катерория, Свойство товара, Значение свойства товара
У категории — коллекция свойств товара
У товара — коллекция значений свойств
У свойства товара — ссылка на категорию
У значения свойства — ссылка на свойство товара
Или вы хотите чтобы клиент сам дописывал нужные классы если он решил внести в ассортимент новый тип товаров? Это прекрасный способ привязать к себе клиента, но до поры до времени.
public interface IDrawer
{
public void draw(HtmlPage page)
{ /*...*/ }
}
а просто:
public interface IDrawer
{
public void draw(HtmlPage page);
}
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re: Цифровой товар, выделить суперклассы
От:
Аноним
Дата:
13.04.06 06:07
Оценка:
Здравствуйте, Foror, Вы писали:
... F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Не то, что б совсем уж имхо...
Самое важное: любое, принятое тобой решение, должно предполагать единообразную обработку различных товаров (реализуют один интерфейс).
В остальном я бы предложил воспользоваться таким правилом: потомок должен создаваться, если у него будут уникальные поля и методы. Например, у фильма: trailer и ShowTrailer(). Если нет — в товар добавь тип товара.
Еще один нюанс. Подумай в каком направлении будет происходить развитие. Возможно, лучше сразу заложить в арихитектуру будущие изменения.
Здравствуйте, UnIc0dE, Вы писали:
UIE>Так что как поизвращаться с наследниками у автора проблемы не возникает. Наоборот, он старается от этого избавиться. И правильно делает. Из условия задачи выходит что 3 наследника причиняют больше неудобств чем выгоды.
Из контекста вообще ничего не видно. Пользуясь только информацией о данных, можно только сделать выводы как это может лежать на БД. И то только в классической нормализованной модели. Обычно приходится держать в голове наиболее криминальные транзакции. Делать же выводы по дизайну ООП можно только основываясь на поведении(о чем совершенно не знает г-п Лебедев). Данные в данном случае — вторичны.
Здравствуйте, UnIc0dE, Вы писали:
UIE>По моему глубокому IMHO, создавать наследников следует только если от этого есть какая-то практическая польза, а не тупо передирать патерны из учебника, чтобы получилось красиво.
Полностью согласен с Вашей точкой зрения. Не смотря на то, что данные не важны (и это правильно отметил Глеб Земсков), важно то, как они сгруппированы. Группировка же должна делаться, исходя из удобств использования, а не из того, как эти данные "лежат" в базе данных. Чего, к сожалению, не понимает (и не хочет понять!) Глеб Земсков. Иными словами, пользователь первичен, а база данных — вторична. Проблема оптимизации количества запросов к базе данных хотя и важна, но тоже вторична. И решается она совсем на другом системном уровне.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Группировка же должна делаться, исходя из удобств использования,
Ну вот, хоть что-то. КЛ>а не из того, как эти данные "лежат" в базе данных. Чего, к сожалению, не понимает (и не хочет понять!) Глеб Земсков.
И где это ты слышал? О независимости модели данных и объектной модели слышал? КЛ>Иными словами, пользователь первичен, а база данных — вторична.
Интересная постановка задачи. Никогда об этом не задумывался.
Здравствуйте, Foror, Вы писали:
F>Делать три сущности, как-то не хочется, да и название не могу им придумать. Может есть какое-то более F>интересное решение?
Я буду делать две сущности + одна абстрактная. Первая сущность может
иметь своё изображение, и товар, который наследует эту сущность, должен выводиться пользователю
с учетом этих данных. Вторая сущность имеет файл и изображение, а значит для товара данного
типа будет свой вывод. От третьей сущности(которая имеет только файл) отказался, т.к. только
одна Музыкальная композиция имеет случай с файлом без изображения. Этот частный случай наследует
напрямую от абстрактного товара.
А делать динамические поля пока не нужно, и вообще наврятли я думаю понадобятся от специфики
продаваемого товара.
Здравствуйте, GlebZ, Вы писали:
GZ>И где это ты слышал? О независимости модели данных и объектной модели слышал?
В русском языке при обращении к незнакомому собеседнику принято употреблять Вы. Это правило вежливости еще никто не отменял.
КЛ>>Иными словами, пользователь первичен, а база данных — вторична. GZ>Интересная постановка задачи. Никогда об этом не задумывался.
Вот именно, что "не задумывались". А при проектировании программы стоит задуматься. Хотя бы по той причине, что программа (и база данных!) создается под предметную область и для Пользователя, а не предметная область и Пользователь перестраиваются под программу. Возможно, эта мысль для Вас кажется новой. Но, тем не менее, она верна.