class Derived_A1 : public Generic_A { ... };
class Derived_A2 : public Generic_A { ... };
class Derived_A3 : public Generic_A { ... };
class Derived_A4 : public Generic_A { ... };
Также существует класс B, который агрегирует в себе объект класса A (а точнее одного из его наследников):
class B
{
Generic_A* a;
};
Но в связи с особенностями предметной области класс B может агрегировать только либо Derived_A1, либо Derived_A2, либо Derived_A3, но не Derived_A4. Также существует класс C:
class C
{
Generic_A* a;
};
Он может агрегировать только либо Derived_A2, либо Derived_A3.
Здравствуйте, dorofeevilya, Вы писали:
D>Как лучше смоделировать такую ситуацию?
Ну первое, что приходит в голову, это выделить два интерейса: SuitableForB и SuitableForC. Соответственно Derived_A1, Derived_A2 и Derived_A3 будут реализовывать SuitableForB, а Derived_A2 и Derived_A3 — SuitableForC.
Здравствуйте, Mazay, Вы писали:
M>Здравствуйте, dorofeevilya, Вы писали:
D>>Как лучше смоделировать такую ситуацию?
M>Ну первое, что приходит в голову, это выделить два интерейса: SuitableForB и SuitableForC. Соответственно Derived_A1, Derived_A2 и Derived_A3 будут реализовывать SuitableForB, а Derived_A2 и Derived_A3 — SuitableForC.
Мне тоже пришла в голову такая идея, но не более того... Не могу представить, что должны из себя представлять интерфейсы SuitableFor*.
Здравствуйте, dorofeevilya, Вы писали:
D>Но в связи с особенностями предметной области класс B может агрегировать только либо Derived_A1, либо Derived_A2, либо Derived_A3, но не Derived_A4. Также существует класс C: D>Он может агрегировать только либо Derived_A2, либо Derived_A3.
D>Как лучше смоделировать такую ситуацию?
Либо плотно подумать над общей архитектурой, либо рассказать, почему такое происходит (выборочная агрегация), либо ввести [маркерные] интерфейсы.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
Здравствуйте, Нахлобуч, Вы писали:
Н>Здравствуйте, dorofeevilya, Вы писали:
D>>Но в связи с особенностями предметной области класс B может агрегировать только либо Derived_A1, либо Derived_A2, либо Derived_A3, но не Derived_A4. Также существует класс C: D>>Он может агрегировать только либо Derived_A2, либо Derived_A3.
D>>Как лучше смоделировать такую ситуацию?
Н>Либо плотно подумать над общей архитектурой, либо рассказать, почему такое происходит (выборочная агрегация), либо ввести [маркерные] интерфейсы.
Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился.
Ситуация такова: необходима модель данных для описания конструкций многоэтажных зданий. Они состоят из отдельных конструктивных элементов: плит, колонн, балок и т. д. Каждый конструктивный элемент (классы B и C), понятно, сделан из какого-либо материала (класс Generic_A, а также унаследованные от него). На мой взгляд вполне логично представить Материал в виде абстракции (класс Generic_A), а затем уточнить его вид в конкретном классе с помощью наследования (классы Derived_A*). Однако, допустим, конструктивному элементу "Плита" могут быть назначены материалы Derived_A1, Derived_A2, Derived_A3, а элементу "Колонна" — Derived_A2 и Derived_A3.
Здравствуйте, dorofeevilya, Вы писали:
D>Как лучше смоделировать такую ситуацию?
Если Вы используете полиморфное наследование, то объект любого производного класса может быть применен там, где может быть применен объект базового класса. Собственно, в этом и заключается LSP (принцип подстановки Барбары Лисков).
Если у Вас он не выполняется, значит что-то спроектировано неверно. В чем конкретно заключается "неверность", сказать трудно — нужны конкретные детали.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, dorofeevilya, Вы писали:
D>>Как лучше смоделировать такую ситуацию? КЛ>Если Вы используете полиморфное наследование, то объект любого производного класса может быть применен там, где может быть применен объект базового класса. Собственно, в этом и заключается LSP (принцип подстановки Барбары Лисков).
КЛ>Если у Вас он не выполняется, значит что-то спроектировано неверно. В чем конкретно заключается "неверность", сказать трудно — нужны конкретные детали.
Детали:
Необходима модель данных для описания конструкций многоэтажных зданий. Они состоят из отдельных конструктивных элементов: плит, колонн, балок и т. д. Каждый конструктивный элемент (классы B и C), понятно, сделан из какого-либо материала (класс Generic_A, а также унаследованные от него). На мой взгляд вполне логично представить Материал в виде абстракции (класс Generic_A), а затем уточнить его вид в конкретном классе с помощью наследования (классы Derived_A*). Однако, допустим, конструктивному элементу "Плита" могут быть назначены материалы Derived_A1, Derived_A2, Derived_A3, а элементу "Колонна" — Derived_A2 и Derived_A3.
Здравствуйте, dorofeevilya, Вы писали:
D>Мне тоже пришла в голову такая идея, но не более того... Не могу представить, что должны из себя представлять интерфейсы SuitableFor*.
По идее это должно зависеть от того, как классы B и C их используют. Если никак не используют, то могут быть пустыми. (Кажется, Нахлобуч именно такие интерфейсы и назвал "маркерными")
Здравствуйте, dorofeevilya, Вы писали:
D>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился.
Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
Здравствуйте, dorofeevilya, Вы писали:
D>Детали: D>Необходима модель данных для описания конструкций многоэтажных зданий. Они состоят из отдельных конструктивных элементов: плит, колонн, балок и т. д. Каждый конструктивный элемент (классы B и C), понятно, сделан из какого-либо материала (класс Generic_A, а также унаследованные от него). На мой взгляд вполне логично представить Материал в виде абстракции (класс Generic_A), а затем уточнить его вид в конкретном классе с помощью наследования (классы Derived_A*). Однако, допустим, конструктивному элементу "Плита" могут быть назначены материалы Derived_A1, Derived_A2, Derived_A3, а элементу "Колонна" — Derived_A2 и Derived_A3.
ИМХО не стоит задавать в коде материиалы, из которых могут быть сделаны конструктивные элементы. Тут вообще можно выделить только два класса — КонструктивныйЭлемент и Материал. Остальное — "типичная ошибка ООП".
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, dorofeevilya, Вы писали:
D>>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился. КЛ>Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
Зачем ввиде таблицы? Тут пока свзяи многие-ко-многим нет. Хотя напрашивается конечно.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, dorofeevilya, Вы писали:
D>>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился. КЛ>Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
А в чем разница? Так или иначе будет возможность назначить неподходящий материал элементу. Как я понимаю, здесь необходимо делать проверку типа материала при назначении его элементу. Вопрос, когда выполнять эту проверку: на этапе компиляции или во время выполнения. Во время выполнения можно сделать так:
class Material
{
virtual MaterialType GetType() = 0;
};
class Material1 : public Material
{
/* Поля класса*/virtual MaterialType GetType() { return MaterialType.Material1; }
};
class Material2 : public Material
{
/* Поля класса*/virtual MaterialType GetType() { return MaterialType.Material2; }
};
// и т. д. для Material3 и Material4class B
{
private Generic_A* material;
public SetMaterial(Generic_A* m)
{
switch (m->GetType())
{
case MaterialType.Material1:
case MaterialType.Material2:
case MaterialType.Material3:
material = m;
break;
default:
throw(); // или что-то еще
}
}
};
А реально ли предоставить эту проверку компилятору?
Здравствуйте, Mazay, Вы писали:
M>ИМХО не стоит задавать в коде материиалы, из которых могут быть сделаны конструктивные элементы. Тут вообще можно выделить только два класса — КонструктивныйЭлемент и Материал. Остальное — "типичная ошибка ООП".
Здравствуйте, dorofeevilya, Вы писали:
D>Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>>Здравствуйте, dorofeevilya, Вы писали:
D>>>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился. КЛ>>Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
D>А в чем разница? Так или иначе будет возможность назначить неподходящий материал элементу. Как я понимаю, здесь необходимо делать проверку типа материала при назначении его элементу. Вопрос, когда выполнять эту проверку: на этапе компиляции или во время выполнения. Во время выполнения можно сделать так:
// страшный свич ы ужасе поскипан
D>А реально ли предоставить эту проверку компилятору?
Реально — через [маркерные] интерфейсы, но незачем. Лучше если это можно будет настраивать без изменения кода.
Посмотрев на этот код, понял о какой таблице говорил Кирил. Я думал о типичной разводке связи многие-ко-многим, а имелась ввиду таблица соответствия идентификатора элемента к списку допустимых для него материалов.
В коде у тебя должно быть что-то вроде такого мэпа:
Здравствуйте, dorofeevilya, Вы писали:
D>Здравствуйте, Mazay, Вы писали:
M>>ИМХО не стоит задавать в коде материиалы, из которых могут быть сделаны конструктивные элементы. Тут вообще можно выделить только два класса — КонструктивныйЭлемент и Материал. Остальное — "типичная ошибка ООП".
D>Не понял...
Здравствуйте, dorofeevilya, Вы писали:
D>А в чем разница? Так или иначе будет возможность назначить неподходящий материал элементу.
А зачем данные, которые, к тому же, могут еще и поменяться хардкодить в код? По последним данным известно около 7 млн. органических и 160 тыс. неорганических соединений естественного и искусственного происхождения. Согласитесь, неразумно каждое из них представлять в виде класса.
Поэтому конкретные данные должны содержаться в файле или в базе данных. К данным относятся и типы веществ, и типы конструкций, и допустимость веществ в определенных конструкциях. А код, в соответствии с этими данными, должен назначать той или иной конструкции то или иное вещество. В соответствии с правилами, которые хранятся в базе данных.
Здравствуйте, Mazay, Вы писали:
M>Посмотрев на этот код, понял о какой таблице говорил Кирил. Я думал о типичной разводке связи многие-ко-многим, а имелась ввиду таблица соответствия идентификатора элемента к списку допустимых для него материалов.
Вы совершенно правы — я писал об обычной реляционной таблице, состоящей из двух колонок:
1) Тип материала.
2) Тип изделия.
В C++-коде такую таблицу можно представить в виде std::map<,>.
КЛ>А зачем данные, которые, к тому же, могут еще и поменяться хардкодить в код? По последним данным известно около 7 млн. органических и 160 тыс. неорганических соединений естественного и искусственного происхождения. Согласитесь, неразумно каждое из них представлять в виде класса.
Насчет 7160000 соединений мне кажется, что это не отдельные классы, а экземпляры одного из двух классов (class ОргВещество и class НеоргВещество), также как и, допустим, бетоны марки B20 и B25 — два экземпляра class Бетон : Материал, а не два отдельных класса.
КЛ>Поэтому конкретные данные должны содержаться в файле или в базе данных. К данным относятся и типы веществ, и типы конструкций, и допустимость веществ в определенных конструкциях. А код, в соответствии с этими данными, должен назначать той или иной конструкции то или иное вещество. В соответствии с правилами, которые хранятся в базе данных.
А зачем тогда кодировать модель данных вообще, если все содержится в некой базе данных? Или я неправильно Вас понял?
Здравствуйте, Mazay, Вы писали:
M>Здравствуйте, dorofeevilya, Вы писали:
D>>Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>>>Здравствуйте, dorofeevilya, Вы писали:
D>>>>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился. КЛ>>>Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
D>>А в чем разница? Так или иначе будет возможность назначить неподходящий материал элементу. Как я понимаю, здесь необходимо делать проверку типа материала при назначении его элементу. Вопрос, когда выполнять эту проверку: на этапе компиляции или во время выполнения. Во время выполнения можно сделать так:
M>// страшный свич ы ужасе поскипан
D>>А реально ли предоставить эту проверку компилятору? M> Реально — через [маркерные] интерфейсы, но незачем. Лучше если это можно будет настраивать без изменения кода.
M>Посмотрев на этот код, понял о какой таблице говорил Кирил. Я думал о типичной разводке связи многие-ко-многим, а имелась ввиду таблица соответствия идентификатора элемента к списку допустимых для него материалов. M>В коде у тебя должно быть что-то вроде такого мэпа:
1) Почему не стоит использовать "маркерные" интерфейсы? Чем хуже? Я думал, что предоставить проверку компилятору лучше, чем делать то же самое в рантайме. Объясните, в чем я неправ.
2) И все же. Как использовать такие интерфейсы. Если можно, примерчик какой-нить...
Здравствуйте, dorofeevilya, Вы писали:
D>1) Почему не стоит использовать "маркерные" интерфейсы? Чем хуже? Я думал, что предоставить проверку компилятору лучше, чем делать то же самое в рантайме. Объясните, в чем я неправ.
В том что полагаешь, что нашёл серебряную пулю. В некоторых случаях лучше отдавать проверку компилятору, а в некоторых оставлять на рантайм. Это зависит от предметной области. Вот ты выше предложил разбиения на class ОргВещество и class НеоргВещество или на class Бетон и class ЕщёЧтоТоТогоЖеУровняАбстракции. А мы с высоты собственного опыта советуем тебе вообще не разбивать, а оставить только class Вещество.
D>2) И все же. Как использовать такие интерфейсы. Если можно, примерчик какой-нить...
Да какой-тут примерчик:
class Generic_A
{
void foo() {/* код */}
void bar() {/* код */}
void car() {/* код */}
};
class SuitableForB //чисто абстрактный класс
{
virtual void foo() = 0;
virtual void bar() = 0;
//всё. больше ничего.
}
class SuitableForС //чисто абстрактный класс
{
//вообще может быть пустым.
}
class Derived_A1 : public Generic_A, SuitableForB { ... };
class Derived_A2 : public Generic_A, SuitableForB, SuitableForC { ... };
class Derived_A3 : public Generic_A, SuitableForB, SuitableForC { ... };
class Derived_A4 : public Generic_A { ... };
class B
{
SuitableForB * a;
void use() { a->foo(); a->bar(); a->сar();}
};
class C
{
SuitableForC * a;
void use() { a->сar(); }
};