Здравствуйте, dorofeevilya, Вы писали:
D>А в чем разница? Так или иначе будет возможность назначить неподходящий материал элементу.
А зачем данные, которые, к тому же, могут еще и поменяться хардкодить в код? По последним данным известно около 7 млн. органических и 160 тыс. неорганических соединений естественного и искусственного происхождения. Согласитесь, неразумно каждое из них представлять в виде класса.
Поэтому конкретные данные должны содержаться в файле или в базе данных. К данным относятся и типы веществ, и типы конструкций, и допустимость веществ в определенных конструкциях. А код, в соответствии с этими данными, должен назначать той или иной конструкции то или иное вещество. В соответствии с правилами, которые хранятся в базе данных.
Здравствуйте, dorofeevilya, Вы писали:
D>Детали: D>Необходима модель данных для описания конструкций многоэтажных зданий. Они состоят из отдельных конструктивных элементов: плит, колонн, балок и т. д. Каждый конструктивный элемент (классы B и C), понятно, сделан из какого-либо материала (класс Generic_A, а также унаследованные от него). На мой взгляд вполне логично представить Материал в виде абстракции (класс Generic_A), а затем уточнить его вид в конкретном классе с помощью наследования (классы Derived_A*). Однако, допустим, конструктивному элементу "Плита" могут быть назначены материалы Derived_A1, Derived_A2, Derived_A3, а элементу "Колонна" — Derived_A2 и Derived_A3.
ИМХО не стоит задавать в коде материиалы, из которых могут быть сделаны конструктивные элементы. Тут вообще можно выделить только два класса — КонструктивныйЭлемент и Материал. Остальное — "типичная ошибка ООП".
Здравствуйте, dorofeevilya, Вы писали:
D>Здравствуйте, Mazay, Вы писали:
M>>ИМХО не стоит задавать в коде материиалы, из которых могут быть сделаны конструктивные элементы. Тут вообще можно выделить только два класса — КонструктивныйЭлемент и Материал. Остальное — "типичная ошибка ООП".
D>Не понял...
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>>Над общей архитектурой уже давно плотно думаю На этой проблеме застопорился. КЛ>Почему материал нельзя представить в виде идентификатора, а зависимость между изделием и материалом — в виде таблицы?
Зачем ввиде таблицы? Тут пока свзяи многие-ко-многим нет. Хотя напрашивается конечно.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, 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>А реально ли предоставить эту проверку компилятору?
Реально — через [маркерные] интерфейсы, но незачем. Лучше если это можно будет настраивать без изменения кода.
Посмотрев на этот код, понял о какой таблице говорил Кирил. Я думал о типичной разводке связи многие-ко-многим, а имелась ввиду таблица соответствия идентификатора элемента к списку допустимых для него материалов.
В коде у тебя должно быть что-то вроде такого мэпа:
Здравствуйте, 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(); }
};
M> ........
M>class C
M>{
M> SuitableForC * a;
M> void use() { /* никак не используем */ }
M>};
M> .........
M>
А если лениво описывать интерфейсы можно так:
class Generic_A
{
void foo() {/* код */}
void bar() {/* код */}
void car() {/* код */}
};
class SuitableForB: public Generic_A
{
//вообще может быть пустым.
}
class SuitableForС: public Generic_A
{
//вообще может быть пустым.
}
class Derived_A1 : public SuitableForB { ... };
class Derived_A2 : public SuitableForB, SuitableForC { ... }; //тут как-то хитро надо "ромбик" разводить, но это решаемо.class Derived_A3 : public 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->foo(); a->bar(); a->сar();}
};
За пример спасибо. Здесь все понятно! Как сам не догадался?! А вот насчет неразделения класса не понимаю, почему наследование в данном случае плохо и как тогда работать с единственным общим классом Материал.
Здравствуйте, dorofeevilya, Вы писали:
D>За пример спасибо. Здесь все понятно! Как сам не догадался?! А вот насчет неразделения класса не понимаю, почему наследование в данном случае плохо и как тогда работать с единственным общим классом Материал.
Ну это я "с высоты собственного опыта" говорю. То есть я полагаю, что в дальнейшем надобности в таком разделении не возникнет. Конечно, тебе виднее, ты домен знаешь лучше. Но скажи, какие методы будут в этих подклассах тогда? Я просто не представляю, что можно делать с Материалом кроме как копировать/сравнивать. Если это всего лишь хранилище специфичных данных, то ради этого не стоит жертвовать рантаймиовой настраимовостью. Лучше какой-нибудь проперти-мэп присобачить. 100% потом проще будет и манипулировать этим и на UI с этим работать.
Здравствуйте, dorofeevilya, Вы писали:
D>Насчет 7160000 соединений мне кажется, что это не отдельные классы, а экземпляры одного из двух классов (class ОргВещество и class НеоргВещество), также как и, допустим, бетоны марки B20 и B25 — два экземпляра class Бетон : Материал, а не два отдельных класса.
Видите ли, с точки зрения проектируемой программы нам не важно, какое это вещество — органическое или неорганическое. Нам важно другое — является ли это вещество материалом, из которого можно сделать конструкцию. И, очевидно, если ответ на последний вопрос "нет", то мы это вещество просто не рассматриваем.
Поэтому и имеем лишь один класс — Материал, а если быть точным, то — Материал для Конструкций.
D>А зачем тогда кодировать модель данных вообще, если все содержится в некой базе данных? Или я неправильно Вас понял?
И я не понимаю: "Зачем?" Если модель данных ничего не делает, то и не нужна такая модель. Мне кажется, что при проектировании правильнее ограничиваться минимальным количеством сущностей. Если у Вас класс не имеет каких-либо обязанностей, смело удалите его. Если несколько классов имеют одинаковые обязанности, то объедините их в один класс, а все лишние — выкините.
Т.е. пишите и оставляйте в программе только те классы, которые у Вас что-то делают.
Здравствуйте, Кирилл Лебедев, Вы писали:
КЛ>Здравствуйте, dorofeevilya, Вы писали:
D>>Насчет 7160000 соединений мне кажется, что это не отдельные классы, а экземпляры одного из двух классов (class ОргВещество и class НеоргВещество), также как и, допустим, бетоны марки B20 и B25 — два экземпляра class Бетон : Материал, а не два отдельных класса. КЛ>Видите ли, с точки зрения проектируемой программы нам не важно, какое это вещество — органическое или неорганическое. Нам важно другое — является ли это вещество материалом, из которого можно сделать конструкцию. И, очевидно, если ответ на последний вопрос "нет", то мы это вещество просто не рассматриваем.
КЛ>Поэтому и имеем лишь один класс — Материал, а если быть точным, то — Материал для Конструкций.
D>>А зачем тогда кодировать модель данных вообще, если все содержится в некой базе данных? Или я неправильно Вас понял? КЛ>И я не понимаю: "Зачем?" Если модель данных ничего не делает, то и не нужна такая модель. Мне кажется, что при проектировании правильнее ограничиваться минимальным количеством сущностей. Если у Вас класс не имеет каких-либо обязанностей, смело удалите его. Если несколько классов имеют одинаковые обязанности, то объедините их в один класс, а все лишние — выкините.
КЛ>Т.е. пишите и оставляйте в программе только те классы, которые у Вас что-то делают.
В принципе, я уже начинаю понимать о чем Вы говорите. Действительно, по сути производные классы материалов являются всего лишь хранилищами данных (физических характеристик) какого-либо типа материала. Но как выглядит реализация всего этого дела, я еще не могу понять. Может быть я не очень понятно задачу описал...
Здравствуйте, dorofeevilya, Вы писали:
D>В принципе, я уже начинаю понимать о чем Вы говорите. Действительно, по сути производные классы материалов являются всего лишь хранилищами данных (физических характеристик) какого-либо типа материала. Но как выглядит реализация всего этого дела, я еще не могу понять. Может быть я не очень понятно задачу описал...
В БД это будет:
Широченная таблица с колонками для хранения всех возможных параметров. Если нормальная СУБД и с данными планируется активно работать, то на этом лучше и остановиться
Все параметры хранятся в одном строковом атрибуте. Например в XML.
Разбить все возможные параметры материала на класса и выделить под их хранение отдельные таблицы (по таблице на каждый тип параметров) и плюс таблицы для связи всего этого барахла. (ИМХО выглядеть это будет красиво, но работать с этим будет неудобно)
А в коде тут вообще куча вариантов. Но в основе будет что-то типа map<Parameter, ParameterValue> .