Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.219.236.62] |
|
Страницы: (8) « Первая ... 2 3 [4] 5 6 ... Последняя » все ( Перейти к последнему сообщению ) |
Сообщ.
#46
,
|
|
|
Цитата Wound @ 1) Интересно, каким боком тут наследование можно засунуть? 2) В любом хеловорде всегда все очевидно. Засунуть наследование можно, только вот зачем? Если ты хочешь написать как можно больше дублирующего кода, то можно идти в этом направлении. Но зачем? Цитата Wound @ Представь что нужно сделать еще одну машину, ровно такую же, как и MBS500, на ее же базе, но называться она будет MBS501, в ней будет все, тоже самое что и в твоей MBS500, но добавляется несколько деталей, которых нет у MBS500, например там люк, кондиционер, пятая или шестая дверь и т.д. Как при такой схеме классов ты это выразишь? interface ICar { string Vendor { get; } string Model { get; } ICoachwork Coachwork { get; } IEngine Engine { get; } } interface ICoachwork { IEnumerable<IDoor> Doors {get;} ISunroof Sunroof { get; } } interface ISunroof { bool IsGlas { get; set; } bool IsElectric {get; set; } } class abstract Coachwork : ICoachwork { private List<IDoor> _doors = new List<IDoors>(); IEnumerable<IDoor> Doors {get { return _doors; } } ISunroof Sunroof { get; private set; } public Coachwork (ISunroof sunroof) { Sunroof = sunroof; } protected void AddDoor (IDoor door) { _doors.Add (door); } } class CoachworkLimusine : Coachwork { public CoachworkLimusine (ISubroof sunroof) : base (sunroof) { AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new TrunkDoor ()); } } class CoachworkCombi : Coachwork { public CoachworkCombi (ISubroof sunroof) : base (sunroof) { AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new BackDoor ()); } } class MBS50x { public string Vendor { get {return "Daimler"; } } public string Model { get; private set; } public IEngine Engine {get; private set; } public ICoachwork Coachwork {get; private set; } public MBS50x (string model, ICoachwork coachwork, IEngine engine) { Model = model; Coachwork = coachwork; Engine = engine; } } ну и дальше все это компонуется. например так: Sunroof sunroof = new Sunroof (); sunroof.IsGlas = true; sunroof.IsElectric = true; MBS50x newMB = new MBS50x ("MBS501", new CoachworkCombi(), sunroof); Добавлено Цитата Wound @ Странно что ты Fester читал, читал и даже примера не написал. А я уверен что ты бы написал в рабочем проекте вот такую иерархию: Шеф отвлек звонком Добавлено Тогда уж так: class MBS500 : ICar { public string Vendor { get {return "Daimler"; } } public string Model { get; private set; } public IEngine Engine {get; private set; } public MBS500 (IEngine engine) : this (ingine, "Mercedes-Benz S500") { } public MBS500 (IEngine engine, string model) { Model = model; Engine = engine; } } public class MBS501 : MBS500 { public MBS501 (IEngine engine) : base (engine, "Mercedes-Benz S501") { } /*тут дополнительные примочки какие то*/ } Добавлено Зачем обызательно заменять агрегацию наследованием или наоборот? Для каждой задачи свое решение. |
Сообщ.
#47
,
|
|
|
Цитата Fester @ Засунуть наследование можно, только вот зачем? Вот именно, даже если очень хочется, то смысла от этого вообще нет в твоем примере. Соответственно я и не понял, что ты своим примером хотел показать. Цитата Fester @ Если ты хочешь написать как можно больше дублирующего кода, то можно идти в этом направлении. Но зачем? Ну а зачем ты тогда привел этот пример, который ровным счетом ничего не показывает? Я предположил, что ты имел ввиду, что тут можно наследование прикрутить. А зачем же ты еще приводил этот пример? Тут двигатель даже по смыслу агрегат автомобиля. Цитата Fester @ interface ICar { string Vendor { get; } string Model { get; } ICoachwork Coachwork { get; } IEngine Engine { get; } } interface ICoachwork { IEnumerable<IDoor> Doors {get;} ISunroof Sunroof { get; } } interface ISunroof { bool IsGlas { get; set; } bool IsElectric {get; set; } } class abstract Coachwork : ICoachwork { private List<IDoor> _doors = new List<IDoors>(); IEnumerable<IDoor> Doors {get { return _doors; } } ISunroof Sunroof { get; private set; } public Coachwork (ISunroof sunroof) { Sunroof = sunroof; } protected void AddDoor (IDoor door) { _doors.Add (door); } } class CoachworkLimusine : Coachwork { public CoachworkLimusine (ISubroof sunroof) : base (sunroof) { AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new TrunkDoor ()); } } class CoachworkCombi : Coachwork { public CoachworkCombi (ISubroof sunroof) : base (sunroof) { AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new Door ()); AddDoor (new BackDoor ()); } } class MBS50x { public string Vendor { get {return "Daimler"; } } public string Model { get; private set; } public IEngine Engine {get; private set; } public ICoachwork Coachwork {get; private set; } public MBS50x (string model, ICoachwork coachwork, IEngine engine) { Model = model; Coachwork = coachwork; Engine = engine; } } Ну вот видишь, что и требовалось доказать, ты написал примерно так же, как я за тебя выше. Суть не поменялась. В итоге ты сделал ошибку которая тут и обсуждается, юзнув тут наследование. А ошибка заключается в том, чтоб не использовать наследование классов. По хорошему твой пример должен быть переписан как то вот так: interface ICar { string Vendor {get;} string Model {get;} IEngine Engine { get; } ... } interface IEngine { string Vendor { get; } int Volume { get; } ... } class MBS500 : ICar { public string Vendor { get {return "Daimler"; } } public string Model { get { return "Mercedes-Benz S500"; } } public IEngine Engine {get; private set; } public MBS500 (IEngine engine) { Engine = engine; } } interface IModifiedCar: ICar { void Fire(); } public class MBS501 : IModifiedCar { private MBS500 _modelForUpgrade; public string Vendor { get {return "Daimler"; } } public string Model { get { return "Mercedes-Benz S501"; } } public IEngine Engine {get; private set; } public MBS501(IEngine engine, MBS500 modelForUpgrade) { _modelForUpgrade = modelForUpgrade; Engine = engine; } public string Mashingun {get;set;} public string OtherModifications {get;set;} /*Плюс вот тут ты реализуешь интерфейс ICar, и если какой то метод уже реализован в MBS500 то пишешь примерно такие вот заглушки, предполагается что методы описанные ниже определены в интерфейсе ICar, и реализованы в MBS500*/ public void Drive() { _modelForUpgrade.Drive(); } public void Run() { _modelForUpgrade.Run(); } public int GetSpeed() { _modelForUpgrade.GetSpeed(); } /*Новый метод Fire из интерфейса IModifiedCar*/ public void Fire() { /*тут реализуешь соответствующий функционал*/ } } Добавлено Цитата Fester @ Зачем обызательно заменять агрегацию наследованием или наоборот? Для каждой задачи свое решение. Ну речь зашла о том, что наследование классов - это всегда плохо. Лучше ее избегать всегда и везде, где это возможно. И только если уж совсем приспичит, то можно оставить, и то с нацелом на то, чтоб потом переписать. Добавлено А все из за того, что если ты изменишь базовый класс, то можешь сломать потомков, наследование приводит к очень сильной связи объектов, и повлиять на это не возможно никак. Т.е. по сути наследование классов лучше всегда заменять на паттерн Стратегия. Добавлено Но я с этим тоже не согласен. Потому что в конечном итоге придется очень много писать руками дублирующие методы классов, которые при наследовании должны быть базовыми. И очень хорошо это заметно там, где глубокая иерархия типов. например в Гуи. А если у тебя один уровень иерархии объектов, то скорее всего заменить это на агрегирование не составит труда. а если 3-4-5 и больше, то тут уже начинаются неудобства. И проперти твои не помогут, напротив еще больше запутают. |
Сообщ.
#48
,
|
|
|
Цитата Wound @ А ошибка заключается в том, чтоб не использовать наследование классов. Это ересь какая-то ИМХО. Думаю, что когда говорилось заменять наследование агрегацией, имелось в виду, что не следует делать так: interface IVehicle { } interface IWheelVehicle : IVehicle { } interface IFlyingVehicle : IVehicle { } interface IMotorVehicle : IWheelVehicle { } interface IDiselVehicle : IMotorVehicle { } interface IBensinVehicle : IMotorVehicle { } interface IElectroVehicle : IMotorVehicle { } |
Сообщ.
#49
,
|
|
|
Цитата Fester @ Цитата Wound @ А ошибка заключается в том, чтоб не использовать наследование классов. Это ересь какая-то ИМХО. Думаю, что когда говорилось заменять наследование агрегацией, имелось в виду, что не следует делать так: interface IVehicle { } interface IWheelVehicle : IVehicle { } interface IFlyingVehicle : IVehicle { } interface IMotorVehicle : IWheelVehicle { } interface IDiselVehicle : IMotorVehicle { } interface IBensinVehicle : IMotorVehicle { } interface IElectroVehicle : IMotorVehicle { } Не, не ересь. Есть серьёзные доводы, которые говорят о том, что наследование плохо. А по поводу твоего примера с интерфейсами, ничего криминального в этом нет, т. к. у тебя там наследование интерфейсов, да и по смыслу наследовать так можно. |
Сообщ.
#50
,
|
|
|
Цитата Wound @ А по поводу твоего примера с интерфейсами, ничего криминального в этом нет, т. к. у тебя там наследование интерфейсов, да и по смыслу наследовать так можно. Делать можно, но получится говно код. Не смотря на то, что это интерфейсы Плохо впадать в крайности. |
Сообщ.
#51
,
|
|
|
Цитата Fester @ Делать можно, но получится говно код. Не смотря на то, что это интерфейсы Плохо впадать в крайности. В чем заключается говнокод? По поводу наследовани/агрегирования, например: https://metanit.com/sharp/patterns/1.2.php Цитата При проектировании отношений между классами надо учитывать некоторые общие рекомендации. В частности, вместо наследования следует предпочитать композицию. При наследовании весь функционал класса-наследника жестко определен на этапе компиляции. И во время выполнения программы мы не можем его динамически переопределить. А класс-наследник не всегда может переопределить код, который определен в родительском классе. Композиция же позволяет динамически определять поведение объекта во время выполнения, и поэтому является более гибкой. http://sergeyteplyakov.blogspot.com/2012/12/vs-vs.html Цитата Существует несколько достаточно объективных критериев для определения связности дизайна по диаграмме классов: большие иерархии наследования (глубокие или широкие иерархии), и повсеместное использование композиции, а не агрегации скорее всего говорит о сильно связанном дизайне. Большое количество наследования говорит о том, что проектировщики забыли о старом добром совете Банды Четырех, который сводится к тому, что следует предпочесть агрегацию наследованию, поскольку первая дает большую гибкость и динамичность во время исполнения. https://en.wikipedia.org/wiki/Composition_over_inheritance |
Сообщ.
#52
,
|
|
|
Цитата Wound @ да и по смыслу наследовать так можно Честно говоря - не уверен в логичности этой иерархии. Хотя, могу ошибаться, ибо Джаву не знаю. Но судя, по статье, интерфейсы чем-то напоминают типажи Раста. Скрытый текст Кстати, OpenGL, в доках по Расту написано, что типажи хоть и похожи на интерфейсы Джавы, но имеют отличия. А какие отличия? Интерфейсы определяют поведение. Не пойму зачем в приведенной иерархии связывать "колесо" и "мотор" или "дизель" и "мотор"? У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав? ЗЫ: Да и то, не все, что с двигателем - заправляются, например трамвай. |
Сообщ.
#53
,
|
|
|
Цитата JoeUser @ Честно говоря - не уверен в логичности этой иерархии. Хотя, могу ошибаться, ибо Джаву не знаю. Но судя, по статье, интерфейсы чем-то напоминают типажи Раста. Не знаю что за типажи Раста, но интерфейсы C# это по сути абстрактный базовый класс в С++, у которого все функции чистовиртуальные. Подозреваю что интерфейсы Java, примерно из той же оперы. Цитата JoeUser @ Интерфейсы определяют поведение. Не пойму зачем в приведенной иерархии связывать "колесо" и "мотор" или "дизель" и "мотор"? У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав? В смысле связывать? У машины есть колесо, двигатель, дизель и т.д. Т.е. это все передается извне например через конструктор или еще как то в класс Машина. Или ты о какой иерархии говоришь то? Добавлено Цитата JoeUser @ ЗЫ: Да и то, не все, что с двигателем - заправляются, например трамвай. Ну вот поэтому там будет интерфейс IEngine, от него будут разные реализации - бензиновый, дизель, электрический. А класс у тебя принимает интерфейс IEngine, а уж какую реализацию ты подсунешь - такой двигатель у машины и будет. |
Сообщ.
#54
,
|
|
|
Цитата Wound @ Или ты о какой иерархии говоришь то? О иерархии интерфейсов, что была в приведенном кусе кода. |
Сообщ.
#55
,
|
|
|
Цитата JoeUser @ О иерархии интерфейсов, что была в приведенном кусе кода. В каком именно? В том что Fester привел? А блин, я кстати с телефона глядел и увидел почему то только три первых интерфейса, и мне подумалось что там типа: ТранспортноеСредство КолесноеТранспортноеСредство ЛетающееТранспортноеСредство И из этого сделал вывод. А щас глянул, там у него целая портянка. Ну да, такое конечно лишено смысла. |
Сообщ.
#56
,
|
|
|
Цитата Wound @ В каком именно? В том что Fester привел? Ага |
Сообщ.
#57
,
|
|
|
Цитата JoeUser @ Ага Я через телефон его сообщение смотрел, у меня код не полностью раскрылся, и я думал речь идет о трех интерфейсах. Типа там ТранспортноеСредство, Летающее и Наземное. И писал исключительно их этих соображений, а щас прочитал твое сообщение про моторы, дизели, отмотал выше и понял что там целая портянка интерфейсов. Такое естественно писать тупо и это говнокод. |
Сообщ.
#58
,
|
|
|
Цитата Wound @ В чем заключается говнокод? Если реализовывать всю эту структуру, то получится много дублируемого кода. Однако, это не значит, что наследование - это плохо. Например, я сейчас пытаюсь пробить шефа на то, чтобы реазизовать примерно такую структуру сообщений interface IEvent { IEventType EventType {get;} string Textoverlay {get;} string AlarmMessage {get;} IDatabaseDto DatabaseDto {get;} ну и еще пара пропертей } class ShoeEvent : IEvent { } class GameEvent : SheEvent { } class CardDrawEvent : GameEvent { Rank Rank { get; } Suit Suit { get; } ... } class DiscardCardEvent : CardDrawEvent { } class PlayerCardEvent : CardDrawEvent { } class BankerCardEvent : CardDrawEvent { } class GameResultEvent : GameEvent { } class ShoeErrorEvent : ShoeEvent { } class ShoeStateEvent : ShoeEvent { } итд Решения при помощи агрегации я тут не вижу. Моя цель в том, чтобы каждый отдельный класс для себя генерировал Textoverlay, AlarmMessage, DatabaseDto и другие общие поля, которые необходимы для обработки сообщения. При этом, GameResultEvent должен быть "наполен" эвентами типа CardDrawEvent и мне надо различать что это был за карта - игрока, банка или на "выбрас". Цитата JoeUser @ У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав? Ты прав, но тольк от части Если ты смотришь на транспортное средство, как на средство передвижения, то да - "перемещаться", "заправляться", "расходоваться топливо". Если же смотреть на транспортное средство, как на продукт, которые надо произвести, то "перемещаться", "заправляться", "расходоваться топливо" соввем не важно важна конфиругация кузова, надпись с названием и цвет обивки Другое дело, что все это логично было добавлять агрегацией, а не наследованием |
Сообщ.
#59
,
|
|
|
Цитата Fester @ Решения при помощи агрегации я тут не вижу. Моя цель в том, чтобы каждый отдельный класс для себя генерировал Textoverlay, AlarmMessage, DatabaseDto и другие общие поля, которые необходимы для обработки сообщения. При этом, GameResultEvent должен быть "наполен" эвентами типа CardDrawEvent и мне надо различать что это был за карта - игрока, банка или на "выбрас". Ну вот везде пишут - что такого, что ты предлагаешь надо избегать. Если будут переделки в базовых класах - то это повлечет кучу проблем. Лично я не люблю делать такие большие иерархии наследования. Слишком запутанно получается, слишком большая связанность классов. На лицо нарушение принципов SOLID. Я не знаю всей специфики твоей задачи, но обычно всякие ивенты решаются с помощью шаблона Посетитель(Visitor) или Наблюдатель(Observer). Возможно тебе нужно пересмотреть дизайн. Слишком много у тебя наследования. ИМХО. Добавлено Цитата Fester @ Решения при помощи агрегации я тут не вижу. Так по сути любое наследование можно заменить агрегацией. Просто связь будет не такая тесная, как в случае с наследованием. |
Сообщ.
#60
,
|
|
|
Хорошо, когда язык поддерживает агрегацию искаропки, да? Вот и ответь теперь, что тебе мешает сделать так же при наследовании реализаций. Гм, ну да... хорошо, когда язык поддерживает множественное наследование реализаций искаропки.
Добавлено Цитата applegame @ Я его юзаю регулярно. Вот только пробелмы ромбов не встречал. Ромбы были, однако. Обычно – при наследовании абстрактных классов сиречь интерфейсов, где оно и должно в общем-то применяться в обязательном порядке.Множественное наследование, кроме как наследование интерфейсов, практически не встречалось, ибо как-то нужды в таких извратах не возникало. Внимательный читатель, естественно, заметит, что в моём примере интерфейсы наследуются невируально. Ну так это и было сделано намерено, т.к. это разные интерфейсы. Жаль, что никто не обратил на это внимания. Ибо при "правильно оформленном" наследовании от интерфейса компилятор немедленно бы ткнул программера пальцем в конфликт неоднозначности. Беда в том, что заранее далеко не всегда знаешь, разные ли это интерфейсы или следующие версии одного и того же базового. Вот типа как вот так: struct IBaseInterface { virtual void madeMeCool() = 0; }; struct ICoolInterface : IBaseInterface { /* тут что-то оригинальное своё */ } struct ICoolerInterface: IBaseInterface { /* тут что-то совсем другое своё */ } struct CCoolImpl: virtual ICoolInterface, virtual ICoolerInterface { /* всё сделано по правилам, и ничего, как говорится, не предвещало */ }; Добавлено Цитата Wound @ Та ладно тебе. Всегда плохо беспричинная сильная связь между сущностями, а наследование всегда сильно связывает предка с наследниками. Если сильная связь подразумевается, как в лице гуёвых иерархий, например, когда кнопка она же окно, то наследование не привнесёт дополнительного дисбаланса в архитектуру. Переделывай базовый класс окна на здоровье, кнопки автоматом подхватят все модификации, и разве не это ожидается в итоге? Ну речь зашла о том, что наследование классов - это всегда плохо. |