На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (8) « Первая ... 2 3 [4] 5 6 ... Последняя » все  ( Перейти к последнему сообщению )  
> Инкапсуляция vs Агрегирование , размышления в различных реализациях (языках программирования)
   
Что считаете лучшим?
Гости не могут просматривать результаты голосования.
Гости не могут голосовать 
    Цитата Wound @
    1) Интересно, каким боком тут наследование можно засунуть?
    2) В любом хеловорде всегда все очевидно.

    Засунуть наследование можно, только вот зачем?


    Цитата Wound @
    По твоему этот пример можно переписать как:

    Если ты хочешь написать как можно больше дублирующего кода, то можно идти в этом направлении. Но зачем?


    Цитата Wound @
    Представь что нужно сделать еще одну машину, ровно такую же, как и MBS500, на ее же базе, но называться она будет MBS501, в ней будет все, тоже самое что и в твоей MBS500, но добавляется несколько деталей, которых нет у MBS500, например там люк, кондиционер, пятая или шестая дверь и т.д. Как при такой схеме классов ты это выразишь?


    ExpandedWrap disabled
      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;
         }
      }


    ну и дальше все это компонуется.
    например так:
    ExpandedWrap disabled
      Sunroof sunroof = new Sunroof ();
      sunroof.IsGlas = true;
      sunroof.IsElectric = true;
       
      MBS50x newMB = new MBS50x ("MBS501", new CoachworkCombi(), sunroof);


    Добавлено
    Цитата Wound @
    Странно что ты Fester читал, читал и даже примера не написал. А я уверен что ты бы написал в рабочем проекте вот такую иерархию:

    Шеф отвлек звонком :)

    Добавлено
    Цитата Wound @
    Да и тут бы ты ее написал скорее всего - потому что это логично.


    Тогда уж так:
    ExpandedWrap disabled
      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")
         {
         }
       
         /*тут дополнительные примочки какие то*/
      }


    Добавлено
    Цитата Wound @
    А речь идет о том, что так делать нельзя, потому что это приводит к ошибкам.

    Зачем обызательно заменять агрегацию наследованием или наоборот? Для каждой задачи свое решение.
      Цитата Fester @
      Засунуть наследование можно, только вот зачем?

      Вот именно, даже если очень хочется, то смысла от этого вообще нет в твоем примере. Соответственно я и не понял, что ты своим примером хотел показать.

      Цитата Fester @
      Если ты хочешь написать как можно больше дублирующего кода, то можно идти в этом направлении. Но зачем?

      Ну а зачем ты тогда привел этот пример, который ровным счетом ничего не показывает? Я предположил, что ты имел ввиду, что тут можно наследование прикрутить. А зачем же ты еще приводил этот пример?
      Тут двигатель даже по смыслу агрегат автомобиля.


      Цитата Fester @
      ExpandedWrap disabled
        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;
           }
        }

      Ну вот видишь, что и требовалось доказать, ты написал примерно так же, как я за тебя выше. Суть не поменялась. В итоге ты сделал ошибку которая тут и обсуждается, юзнув тут наследование. А ошибка заключается в том, чтоб не использовать наследование классов.
      По хорошему твой пример должен быть переписан как то вот так:

      ExpandedWrap disabled
        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 и больше, то тут уже начинаются неудобства. И проперти твои не помогут, напротив еще больше запутают.
      Сообщение отредактировано: Wound -
        Цитата Wound @
        А ошибка заключается в том, чтоб не использовать наследование классов.

        Это ересь какая-то :) ИМХО.

        Думаю, что когда говорилось заменять наследование агрегацией, имелось в виду, что не следует делать так:

        ExpandedWrap disabled
          interface IVehicle
          {
          }
           
          interface IWheelVehicle : IVehicle
          {
          }
           
          interface IFlyingVehicle : IVehicle
          {
          }
           
          interface IMotorVehicle : IWheelVehicle
          {
          }
           
          interface IDiselVehicle : IMotorVehicle
          {
          }
           
          interface IBensinVehicle : IMotorVehicle
          {
          }
           
          interface IElectroVehicle : IMotorVehicle
          {
          }
          Цитата Fester @
          Цитата Wound @
          А ошибка заключается в том, чтоб не использовать наследование классов.

          Это ересь какая-то :) ИМХО.

          Думаю, что когда говорилось заменять наследование агрегацией, имелось в виду, что не следует делать так:

          ExpandedWrap disabled
            interface IVehicle
            {
            }
             
            interface IWheelVehicle : IVehicle
            {
            }
             
            interface IFlyingVehicle : IVehicle
            {
            }
             
            interface IMotorVehicle : IWheelVehicle
            {
            }
             
            interface IDiselVehicle : IMotorVehicle
            {
            }
             
            interface IBensinVehicle : IMotorVehicle
            {
            }
             
            interface IElectroVehicle : IMotorVehicle
            {
            }

          Не, не ересь. Есть серьёзные доводы, которые говорят о том, что наследование плохо. А по поводу твоего примера с интерфейсами, ничего криминального в этом нет, т. к. у тебя там наследование интерфейсов, да и по смыслу наследовать так можно.
            Цитата Wound @
            А по поводу твоего примера с интерфейсами, ничего криминального в этом нет, т. к. у тебя там наследование интерфейсов, да и по смыслу наследовать так можно.

            Делать можно, но получится говно код. Не смотря на то, что это интерфейсы :)

            Плохо впадать в крайности.
              Цитата 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
                Цитата Wound @
                да и по смыслу наследовать так можно

                Честно говоря - не уверен в логичности этой иерархии. Хотя, могу ошибаться, ибо Джаву не знаю. Но судя, по статье, интерфейсы чем-то напоминают типажи Раста.
                Скрытый текст
                Кстати, OpenGL, в доках по Расту написано, что типажи хоть и похожи на интерфейсы Джавы, но имеют отличия. А какие отличия?

                Интерфейсы определяют поведение. Не пойму зачем в приведенной иерархии связывать "колесо" и "мотор" или "дизель" и "мотор"? У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав?

                ЗЫ: Да и то, не все, что с двигателем - заправляются, например трамвай.
                  Цитата JoeUser @
                  Честно говоря - не уверен в логичности этой иерархии. Хотя, могу ошибаться, ибо Джаву не знаю. Но судя, по статье, интерфейсы чем-то напоминают типажи Раста.

                  Не знаю что за типажи Раста, но интерфейсы C# это по сути абстрактный базовый класс в С++, у которого все функции чистовиртуальные. Подозреваю что интерфейсы Java, примерно из той же оперы.

                  Цитата JoeUser @
                  Интерфейсы определяют поведение. Не пойму зачем в приведенной иерархии связывать "колесо" и "мотор" или "дизель" и "мотор"? У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав?

                  В смысле связывать? У машины есть колесо, двигатель, дизель и т.д. Т.е. это все передается извне например через конструктор или еще как то в класс Машина.
                  Или ты о какой иерархии говоришь то?

                  Добавлено
                  Цитата JoeUser @
                  ЗЫ: Да и то, не все, что с двигателем - заправляются, например трамвай.

                  Ну вот поэтому там будет интерфейс IEngine, от него будут разные реализации - бензиновый, дизель, электрический.
                  А класс у тебя принимает интерфейс IEngine, а уж какую реализацию ты подсунешь - такой двигатель у машины и будет.
                    Цитата Wound @
                    Или ты о какой иерархии говоришь то?

                    О иерархии интерфейсов, что была в приведенном кусе кода.
                      Цитата JoeUser @
                      О иерархии интерфейсов, что была в приведенном кусе кода.

                      В каком именно? В том что Fester привел? А блин, я кстати с телефона глядел и увидел почему то только три первых интерфейса, и мне подумалось что там типа:
                      ТранспортноеСредство
                      КолесноеТранспортноеСредство
                      ЛетающееТранспортноеСредство

                      И из этого сделал вывод. А щас глянул, там у него целая портянка. Ну да, такое конечно лишено смысла.
                        Цитата Wound @
                        В каком именно? В том что Fester привел?

                        Ага
                          Цитата JoeUser @
                          Ага

                          Я через телефон его сообщение смотрел, у меня код не полностью раскрылся, и я думал речь идет о трех интерфейсах. Типа там ТранспортноеСредство, Летающее и Наземное. И писал исключительно их этих соображений, а щас прочитал твое сообщение про моторы, дизели, отмотал выше и понял что там целая портянка интерфейсов. Такое естественно писать тупо и это говнокод.
                            Цитата Wound @
                            В чем заключается говнокод?

                            Если реализовывать всю эту структуру, то получится много дублируемого кода.

                            Однако, это не значит, что наследование - это плохо.
                            Например, я сейчас пытаюсь пробить шефа на то, чтобы реазизовать примерно такую структуру сообщений
                            ExpandedWrap disabled
                              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 @
                            У всех транспортных средств есть только одно общее поведение "перемещаться". Те, которые с двигателем - "заправляться", "расходовать топливо". А все остальное, например - способ перемещения, тип топлива, я бы отнес к свойствам уже классов. Я не прав?

                            Ты прав, но тольк от части :) Если ты смотришь на транспортное средство, как на средство передвижения, то да - "перемещаться", "заправляться", "расходоваться топливо". Если же смотреть на транспортное средство, как на продукт, которые надо произвести, то "перемещаться", "заправляться", "расходоваться топливо" соввем не важно :) важна конфиругация кузова, надпись с названием и цвет обивки ;)
                            Другое дело, что все это логично было добавлять агрегацией, а не наследованием :)
                              Цитата Fester @
                              Решения при помощи агрегации я тут не вижу. Моя цель в том, чтобы каждый отдельный класс для себя генерировал Textoverlay, AlarmMessage, DatabaseDto и другие общие поля, которые необходимы для обработки сообщения.
                              При этом, GameResultEvent должен быть "наполен" эвентами типа CardDrawEvent и мне надо различать что это был за карта - игрока, банка или на "выбрас".

                              Ну вот везде пишут - что такого, что ты предлагаешь надо избегать. Если будут переделки в базовых класах - то это повлечет кучу проблем. Лично я не люблю делать такие большие иерархии наследования. Слишком запутанно получается, слишком большая связанность классов. На лицо нарушение принципов SOLID.

                              Я не знаю всей специфики твоей задачи, но обычно всякие ивенты решаются с помощью шаблона Посетитель(Visitor) или Наблюдатель(Observer). Возможно тебе нужно пересмотреть дизайн. Слишком много у тебя наследования. ИМХО.

                              Добавлено
                              Цитата Fester @
                              Решения при помощи агрегации я тут не вижу.

                              Так по сути любое наследование можно заменить агрегацией. Просто связь будет не такая тесная, как в случае с наследованием.
                              Сообщение отредактировано: Wound -
                                Цитата Wound @
                                Ну на плюсах как раз таки есть множественное наследование. В C# напишем:
                                ...
                                Хорошо, когда язык поддерживает агрегацию искаропки, да? Вот и ответь теперь, что тебе мешает сделать так же при наследовании реализаций. Гм, ну да... хорошо, когда язык поддерживает множественное наследование реализаций искаропки.

                                Добавлено
                                Цитата applegame @
                                Множественное наследование, кроме как наследование интерфейсов, практически не встречалось, ибо как-то нужды в таких извратах не возникало.
                                Я его юзаю регулярно. Вот только пробелмы ромбов не встречал. Ромбы были, однако. Обычно – при наследовании абстрактных классов сиречь интерфейсов, где оно и должно в общем-то применяться в обязательном порядке.
                                Внимательный читатель, естественно, заметит, что в моём примере интерфейсы наследуются невируально. Ну так это и было сделано намерено, т.к. это разные интерфейсы. Жаль, что никто не обратил на это внимания. Ибо при "правильно оформленном" наследовании от интерфейса компилятор немедленно бы ткнул программера пальцем в конфликт неоднозначности.
                                Беда в том, что заранее далеко не всегда знаешь, разные ли это интерфейсы или следующие версии одного и того же базового. Вот типа как вот так:
                                ExpandedWrap disabled
                                  struct IBaseInterface
                                  {
                                    virtual void madeMeCool() = 0;
                                  };
                                   
                                  struct ICoolInterface  : IBaseInterface { /* тут что-то оригинальное  своё */ }
                                  struct ICoolerInterface: IBaseInterface { /* тут что-то совсем другое своё */ }
                                   
                                  struct CCoolImpl: virtual ICoolInterface, virtual ICoolerInterface
                                  {
                                    /* всё сделано по правилам, и ничего, как говорится, не предвещало */
                                  };
                                Тут хоть ставь virtual, хоть не ставь – всегда найдётся фэйл-сценарий

                                Добавлено
                                Цитата Wound @
                                Ну речь зашла о том, что наследование классов - это всегда плохо.
                                Та ладно тебе. Всегда плохо беспричинная сильная связь между сущностями, а наследование всегда сильно связывает предка с наследниками. Если сильная связь подразумевается, как в лице гуёвых иерархий, например, когда кнопка она же окно, то наследование не привнесёт дополнительного дисбаланса в архитектуру. Переделывай базовый класс окна на здоровье, кнопки автоматом подхватят все модификации, и разве не это ожидается в итоге?
                                Сообщение отредактировано: Qraizer -
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0631 ]   [ 17 queries used ]   [ Generated: 27.04.24, 15:26 GMT ]