|
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Новый шаблон проектирования: Проектное Многоклассовое генерик-наследование21.03.2018, 17:38. Показов 1290. Ответов 7
Метки нет (Все метки)
Допустим, я разработал некий проект, который назвал "SpecialProject", с классами, которые касаются
проекта, все начинаются с Special... (SpecialDrive, SpecialController, SpecialAlgorithmic и т.д. ) и dll этого проекта отдал другому производителю. Производитель, используя мой dll, написал некую другую программу (у него на выходе будет exe файл), которая использует этот dll. Затем я разработал допустим, другой проект, который назвал "OtherProject", с классами, которые касаются проекта, все начинаются с Other... (OtherDrive, OtherController, OtherAlgorithmic и т.д. ) они унаследованы от соответствующих прототипов классов первого проекта. Тогда я могу дать новую dll производителю, и не требовать его код (да он может и не хотеть его отдавать), для того чтобы переделать, а сказать типа "замени в своем проекте все 'Special' на 'Other' " - и всё заработает. Или и вовсе, дать ему программу, которая удаленно рефлектором вынимет его код, автогенератором заменив 'Special' на 'Other' - на новый, автобилдом - сделает ребилд, и у него готовый проект, который обращается к т.н. "унаследованному проекту". А может даже, и ребилд проекта не потребуется, если в своей dll заменить старый SpecialProject на ModuleProject, а OtherProject на SpecialProject . Ну, это только одна из возможностей, реально же, больше всего именно красота решения нравится. Итак, цель следующая - наследование целого проекта так, чтобы в коде, его использующем, можно было не вникать в код, а просто заменить во всём коде базовые слова. Пусть, базовое слово первого проекта - Module, а унаследованного - Special. Есть цепочка классов, ModuleClass1 ModuleClass2 (принимает и использует ModuleClass1) ModuleClass3 (принимает и использует ModuleClass2) ModuleClass4 (принимает и использует ModuleClass3 ModuleClass2) Наследую целый проект. 4 класса будут такими - SpecialClass1 SpecialClass2 (принимает и использует SpecialClass1) SpecialClass3 (принимает и использует SpecialClass2) SpecialClass4 (принимает и использует SpecialClass3 SpecialClass2). Унаследовать надо целое множество классов, чтобы в любом вызывающем коде достаточно было просто везде заменить слово Module на Special - и всё должно правильно работать. В этом основная цель нового шаблона проектирования: Многоклассовое генерик-наследование. Теперь посмотрим и убедимся, 1) почему не годится - шаблон обычного наследования - и чего здесь не хватает для достижения данной цели, 2) почему не годится - шаблон обычных генерик-классов - и чего здесь не хватает для достижения данной цели. --------------------------------------------------------------------------------------------------------------------- Для простоты, будем использовать множество только из двух классов, 1-й проект назывался ModuleProject, второй наследуемый - SpecialProject. В первом проекте есть всего два класса, ModulePosition + ModuleDrive , а во втором, наследуемые два класса SpecialPosition + SpecialDrive . Понятное дело, что SpecialPosition должен унаследоваться от ModulePosition , далее, SpecialDrive наследуется от ModuleDrive . Каждый из этой пары классов со словом ..Drive - может содержать поля, свой соответствующий "младший класс" проекта - со словом ..Position. Далее пишу пока на С++, потому что в итоге, мне удасться реализовать данный шаблон проектирования на С++, но не знаю, как это сделать на C#. Есть Следующий внешний код, обращающийся к проекту ModuleProject (и классам ModulePosition+ModuleDrive)
с заменой Module на Special - можно было использовать.
1) почему не годится - шаблон обычного наследования - и чего здесь не хватает для достижения данной цели, В проекте ModuleProject (классы ModulePosition+ModuleDrive) - допустим, определены так -
В проекте SpecialProject
Нет! 1) Поле SpecialPosition field; уже существует в базовом класс, а потому его либо НЕЛЬЗЯ переобъявить с другим типом, зависит от компилятора, либо если и можно - то тогда будут ДВА field, с сокрытием имён, к первому можно обратиться через base.field; ПЛОХО - в проекте будут неиспользуемые поля, от базового класса, и будут занимать место в памяти, что тоже плохо, если проект - алгоритмический, и важна производительность + экономия памяти. 2) казалось бы, можно было бы добавить новое поле, типа SpecialPosition field2; и использовать его, но так тоже ПЛОХО, аналогично - в проекте будут неиспользуемые поля, от базового класса, и будут занимать место в памяти, что тоже плохо, если проект - алгоритмический, и важна производительность + экономия памяти. 3) наконец, кто-то скажет, что можно было бы и вовсе не объявлять в классе SpecialDrive - это поле, SpecialPosition field; полагая что оно есть в базовом классе, ModulePosition field; а ссылке на базовый класс можно присваивать объекты производных классов. И вроде бы, требуемый выше внешний код работать будет. Но здесь получается другая нехорошая вещь - в самом коде класса SpecialDrive - у нас field - будет типа ModulePosition , а нам нужно иметь доступ и обрабатывать новые данные, которые есть в SpecialPosition, но нет в ModulePosition (в данном случае это int data1 ). И что, нам везде нужно делать неявное преобразование типов ((SpecialPosition) field).data1 ? Опять же, нехорошо - дополнительные расходы, а проект критичный по производительности должен быть (алгоритмика). Значит, нам нужно, чтобы на уровне компилятора, поле field было типа SpecialPosition , и компилятор так считал (а не на уровне выполнения). --------------------------------------------------------------------------------------------------------------------- 2) почему не годится - шаблон обычных генерик-классов - и чего здесь не хватает для достижения данной цели. Но если не наследоваться, а просто объявить (псевдокод) генерик-классы,
на уровне компилятора, что нам и нужно. Метод MethodGet - имеет разные сигнатуры, а потому переопределяться не будет как виртуальный. Следует ли из этого, что нам и вовсе не надо наследоваться, а достаточно использовать ПРОСТО и только шаблон обычных генерик-классов ? Здесь плохо вот что - в методе MethodGet - придется дублировать код из базового класса! А дублирование кода - это ОЧЕНЬ НЕХОРОШО. (и Фаулер о том же писал). В случае каких-то изменений, нужно помнить все места, где его нужно изменить. Следовательно, было бы хорошо, если строку выше приведенную " Это нужно чтобы не дублировать код!",
можно было использовать. Вывод - и ТОЛЬКО шаблон обычных генерик-классов без наследования - нам не годится. --------------------------------------------------------------------------------------------------------------------- Новый шаблон проектирования: Многоклассовое генерик-наследование. Применительно к примерам, приведенным выше, это означает, что и 1) class SpecialDrive - должен быть НАСЛЕДНИКОМ ModuleDrive, 2) и к тому же, должны быть использованы генерик-классы, т.е. поле field; - у первого распознавалось компилятром как SpecialPosition , а у второго как ModulePosition . Как этого добиться? На C++ это можно сделать, потому что присутствует слово typedef - означает что мы определяем новый тип, который эквивалентен данному, а не унаследован от него. Мы объявим шаблонный класс,
громоздкого TemplateDrive<ModulePosition>. Второй же класс, мы унаследуем
В самом деле, 1) класс генерик, а значит поле field - будет разных типов на уровне компилятора, т.е. если использовать SpecialDrive - то там поле field типа SpecialPosition, а если использовать ModuleDrive - то там поле field типа ModuleDrive - и никакого лишнего использования памяти, сокрытия имён и прочего! 2) можно заметить, что что от чего наследуется, ModuleDrive -> TemplateDrive<SpecialPosition> -> SpecialDrive . В самом деле, ModuleDrive эквивалентен TemplateDrive<ModulePosition> что было объявлено как typedef (в C# могли бы вместо ":" (что означает - унаследован) добавить возможность "~" - (что означает - эквивалентен)). Значит TemplateDrive<SpecialPosition> - это наследник от ModuleDrive - при принципу КОВАРИАНТНОСТИ. Ведь шаблонные подтипы - SpecialPosition - ModulePosition - первый - наследник второго. Ну и в свою очередь, SpecialDrive унаследован уже от TemplateDrive<SpecialPosition> , а значит, SpecialDrive это наследник ModuleDrive - что нам и нужно. Если бы мы не использовали typedef для этой цели, то понятное дело, что наследование TemplateDrive<ModulePosition> ---> ModuleDrive - пошло бы по одной ветке, а наследование TemplateDrive<ModulePosition> ---> TemplateDrive<SpecialPosition> -> SpecialDrive - пошло бы по другой ветке, и SpecialDrive не был бы наследником ModuleDrive , шаблон проектирования, был бы "использование обычных и ТОЛЬКО, генерик-классов". --------------------------------------------------------------------------------------------------------------------- Итак, чтобы использовать этот Новый шаблон проектирования: Многоклассовое генерик-наследование. в C++ - нужно на определенном этапе использовать удобное переопределение типов, используя слово typedef . В C# этого слова нет, непонятно зачем убрали, но есть возможность использовать using типа,
Этот using - нам придётся вставлять в каждый файл с исходным кодом! А если файлов много, + самих переопределяемых классов будет много - будет просто замусоривание кода. --------------------------------------------------------------------------------------------------------------------- Зачем разработчики C#, отказались от использования слова typedef , позволяющее в одном месте один раз объявить эквивалентный тип, и чтобы весь исходный код мог использовать его непонятно, но преимущества typedef , я надеюсь, показал пунктуально.. и доступно. Нужно просто внимательно прочитать. Итак, вот примерно такой код на C++ - и реализует данный шаблон проектирования.
Итак, вот примерно такой код на C++ - и реализует данный шаблон проектирования. И хотелось бы знать, как его наиболее красиво было бы реализовать на C#. Проще говоря, если мы используем обычное наследование классов, к примеру InheritClass : BaseClass то в вызываемом коде, все названия названия одного класса , BaseClass заменяются на InheritClass . Что получается при соблюдении Многоклассового генерик-наследования. как будто, целое связное множество классов, наследуется от другого связного множества (псевдокод) - InheritSet {InheritClass1, InheritClass2, InheritClass3 ...} : BaseSet {BaseClass1, BaseClass2, BaseClass3 ...} и здесь наследование именно такое, что в Тьюринг-полном языке, любой код можно по аналогии, превратить в рабочий код с другим проектом, просто заменив все названия из первого множества - их прототипами из второго множества. Новый шаблон проектирования: Многоклассовое генерик-наследование - можно назвать также, "ПРОЕКТНОЕ наследование", когда целый проект с N классами, наследуется от другого проекта, с N классами-прототипами. Или, объединяя, Новый шаблон проектирования: Проектное Многоклассовое генерик-наследование.
0
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 21.03.2018, 17:38 | |
|
Ответы с готовыми решениями:
7
Шаблон проектирования Prototype. Можно ли по такой реализации сказать, что используется этот шаблон? Шаблон проектирования State - нужен пример
|
|
90 / 90 / 48
Регистрация: 07.12.2011
Сообщений: 215
|
|
| 21.03.2018, 20:32 | |
|
Критика принимается, или не стоит тратить своё время?
А то у меня больше вопросов на хрена это всё, чем мыслей, как это можно сделать
0
|
|
|
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
|
|||
| 21.03.2018, 23:43 [ТС] | |||
|
А то у меня больше вопросов 1) на хрена это всё, чем 2) мыслей, как это можно сделать
Ладно.. Поставлю вопрос проще. 1) зачем это всё - пока оставим на потом . Так как это более сложный вопрос. Если бы это так было ненужно, тогда интересно, зачем разработчики C++ вообще ключевое слово typedef, изобретали.. Ну, пусть разработчики C# посчитали, что это, якобы, ненужно, а потому это убрали (не стали развивать). Потому, пока более интересно - мысли, как это можно сделать если это вообще можно сделать на C# (то, что оказывается, можно сделать на C++), было бы очень хорошо. PS Я описал случай, в чем это может быть по крайней мере, удобным. Про доп. возможности пока не говорим. ----------------------------------------- Дополню. >>Проще говоря, если мы используем обычное наследование классов, к примеру InheritClass : BaseClass то в вызываемом коде, все названия названия одного класса , BaseClass заменяются на InheritClass . Что получается при соблюдении Многоклассового генерик-наследования. как будто, целое связное множество классов, наследуется от другого связного множества (псевдокод) - InheritSet {InheritClass1, InheritClass2, InheritClass3 ...} : BaseSet {BaseClass1, BaseClass2, BaseClass3 ...} и здесь наследование именно такое, что в Тьюринг-полном языке, любой код можно по аналогии, превратить в рабочий код с другим проектом, просто заменив все названия из первого множества - их прототипами из второго множества. >> Здесь получается, именно наследование, а не копипаста. А вот почему в C# нет слова typedef - что есть в C++, это интересный вопрос. И удобство от этого, явно есть - C++ -------------------- template< class Type > class TemplateDrive { public: Type field; Type MethodGet(Type element); }; typedef TemplateDrive<ModulePosition> ModuleDrive; ------------------------------------------ код на C++. Все кто будут использовать TemplateDrive<ModulePosition> - имеют доступ к этой dll. И тут же - объявлено что ModuleDrive - это тип ЭКВИВАЛЕНТНЫЙ TemplateDrive<ModulePosition> Т.е. можно использовать первый вариант, можно второй вариант. В C# нет typedef , а если использовать что то типа using ModuleDrive = TemplateDrive<T> : T is ModulePosition; то это придётся добавлять в каждый файл с кодом. А в C++ один раз объявили два варианта объявления типа и всё. --------------------------------------------- можно подобное объявление и вовсе считать, как class ModuleDrive ~ TemplateDrive<ModulePosition> т.е. просто как не УНАСЛЕДУЕМЫЙ, т.е. class ModuleDrive : TemplateDrive<ModulePosition> а ЭКВИВАЛЕНТНЫЙ (что похоже на ":" ) если после этого слово ModuleDrive нельзя использовать в дочерних dll проекта на C#, то от замены : на ~ никакая функциональность и возможности не пострадают ------------------------------------------- если такой возможности нет ("~" т.е. эквивалентный. и typedef - тоже нету) - то нельзя построить цепочку зависимостей унаследуемых классов, как на C++ , т.е. что от чего наследуется, ModuleDrive -> TemplateDrive<SpecialPosition> -> SpecialDrive . ... SpecialDrive это наследник ModuleDrive - что нам и нужно. (из первого поста темы), Если бы мы не использовали typedef для этой цели, то понятное дело, что наследование TemplateDrive<ModulePosition> ---> ModuleDrive - пошло бы по одной ветке, а наследование TemplateDrive<ModulePosition> ---> TemplateDrive<SpecialPosition> -> SpecialDrive - пошло бы по другой ветке, и SpecialDrive не был бы наследником ModuleDrive , шаблон проектирования, был бы "использование обычных и ТОЛЬКО, генерик-классов". а так у нас, и 1) использование generic , и 2) вместе с тем, есть НАСЛЕДОВАНИЕ т.е. решена задача -
Ну вроде, подробно всё объяснил как мог. Если совсем по простому - то тогда такой вопрос, ИНТЕРЕСНО , а вот почему в C++ есть такое удивительное слово typedef, а в C# его нет? Кажется что всё возможные "причины" отсутствия его - легко преодолеваются разработчиками C# и .NET, а вот новые возможности - появляются. Добавлено через 16 минут Появилась некая критика.
в плане "Очень сложных проектов", включающих в себя 100 библиотек, с разными зависимостями между собой, подобный подход может оказаться очень продуктивным и хорошим. ООП -тоже изобретали, только потому чтобы люди могли написать более сложные проекты, а ЕСЛИ БЫ НЕ ЭТО (и были бы вопросы типа - "на хрена это всё". можно же и без этого обойтись) - то в принципе достаточно было бы ограничиться старым-добрым "Процедурным программированием". В плане теоретически-возможной функциональности - вполне достаточно. Короче, я сам столкнулся с тем, что я вижу как можно упростить разработку и использование унаследуемых проектов. "Зачем" - этот сложный вопрос можно пока оставить. Другой вопрос - "зачем нет typedef в C#" , позволяющий всё упрощать - вот это интересно.
0
|
|||
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|||||
| 22.03.2018, 08:46 | |||||
|
SergeyYN, как же много букафф! А-а-а!
![]() Наследование - это зло. Вместо наследования надо использовать генерализацию. Мыслить не сверху вниз по иерархии классов, а снизу вверх.
0
|
|||||
|
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
|
||||
| 22.03.2018, 10:03 [ТС] | ||||
|
без его использования. А typedef - это всего лишь объявление эквивалентного типа. Тут прозвучала критика, а что будет типа если typedef объявлен в одной библиотеке классов, а потом кто то его переобъявит в другой библиотеке. Не вижу проблемы здесь. Если ваш проект объявлен с reference на другой проект (одна dll будет использовать типы другой dll), то тогда переобъявить тип с помощью typedef и таким же названием нельзя. Компилятор не даст. Других понятных причин почему нет в C# typedef , пока не видно.
0
|
||||
|
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
|
|
| 22.03.2018, 10:40 | |
|
SergeyYN, а в чём проблема-то?
Зачем в C# нужен typedef?Чтобы было проще портировать плюсовый код в шарповский? Если же речь про разработку на шарпе с нуля, то typedef вообще не нужен.Что мне мешает написать сборку (dll), в которой абсолютно все нестатические классы будут internal,а публичными сделать только интерфейсы, простые структуры, перечисления и делегаты? И если в будущем мне приспичит отнаследовать внутренние классы своей сборки от каких-то других, то пользователь моей либы даже не заметит изменений, потому как классы мои ему недоступны, а интерфейс останется прежний. Если же я изменю и интерфейс тоже, то пользователю ничего не останется, кроме как, обматерив меня, переписывать свой код.
0
|
|
|
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
|
||||||||||||||
| 22.03.2018, 17:01 [ТС] | ||||||||||||||
|
с нуля. публичные только интерфейсы, и простые структуры, как вы и предложили. Вот если кто то попробует, (по факту) - перевести код ниже на C# - тогда и поймет в чём проблемы возникают. Код -
При попытке перевести этот код на C++, на C#, вы столкнетесь с одной из следующих 4-х ПРОБЛЕМ. (которые данный паттерн и решает). 1) // и 1++++ не надо создавать новые поля, явно указанные типом SpecialPosition, и впустую тратить память на данное поле - благодаря применению Генерик-классов! -- это строка выше из приведенного кода. Если не применять генерики, то ПРИДЕТСЯ создать доп. поля и заполнить оперативную памяять ненужной информацией, что может быть критично в проектах алгоритмического характера. (там могут быть гигабайты данных). Проблема НОМЕР№ 1. 2) // и 2++++ не надо делать явное преобразование типов каждый раз так ((SpecialPosition)field1).dataNew - благодаря применению Генерик-классов! -- это строка выше из приведенного кода. Если не применять генерики, то в коде будут лишние доп. затраты процессора на явное преобразование типов, что может быть критично в проектах алгоритмического характера. (если там будут миллиарды операций). Проблема НОМЕР№ 2. 3) // и 3++++ дублирования кода нет, благодаря наследованию. (если бы применили только Генерик классы, без наследования, то код пришлось бы определить в 2-х местах). -- это строка выше из приведенного кода. Дублирование кода - это ВСЕГДА плохо. В случае изменений, нужно его менять в нескольких местах. Проблема НОМЕР№ 3. 4) В C# этого typedef нет, непонятно зачем убрали, но есть возможность использовать using , как то типа - using DWORD = System.UInt64; или using ModuleDrive = TemplateDrive<T> : T is ModulePosition; -- Этот using - нам придётся вставлять в каждый файл с исходным кодом! А если файлов много, + самих переопределяемых классов будет много - будет замусоривание кода. Проблема НОМЕР№ 4. На C++ же, в оном месте объявил typedef , как эквивалентный тип, и всё. Вот, решить все выше приведенные проблемы - НОМЕР№ 1, 2,3,4 -- данный паттерн, с использованием typedef и позволяет. Если кто то критикует оттого, что я не описал проблемы, которые паттерн решает - то вот, описал. Добавлено через 9 минут На C++, с использованием такого подхода и typedef - все эти 4 проблемы устраняются. Добавлено через 2 минуты И да, программистам, с таким паттерном, имея 100 библиотек, с разными связями, зависимостями, и наследованием, можно будет разрабатывать намного более сложные проекты. (Это как раньше было - использовать ООП, вместо Процедурного программирования, - на нём тоже можно сделать всё, но для человека - труднее. ) Добавлено через 1 час 8 минут (или всё таки как то можно?) По сути typedef - это был бы аналог алиаса, только объявленный в сборке, и все библиотеки, которые будут использовать эту dll, будут видеть два аналога объявления типа. не надо заставлять программистов, что то типа (псевдокод)
Иделогогия .NET от этого кажется, не пострадала бы.
0
|
||||||||||||||
| 22.03.2018, 17:01 | |
|
Помогаю со студенческими работами здесь
8
Шаблон проектирования JQuery шаблон проектирования java Можно ли применить шаблон проектирования?
Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие.
Ссылка в Linux — это дополнительная запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая. . .
|
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ВВЕДЕНИЕ
Выполняя задание на управление насосной группой заполнения резервуара,. . .
|
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
|
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога
Финальные проекты на Си и на C++:
hello-sdl3-c. zip
hello-sdl3-cpp. zip
Результат:
|
|
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога
MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
|
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд.
Даже если у вас. . .
|
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает
монорепозиторий в котором находятся все исходники.
При создании нового решения, мы просто добавляем нужные проекты
и имеем. . .
|
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение:
В этой книге («Подход, основанный на вариантах использования») Ивар утверждает,
что архитектура программного обеспечения — это
структуры,. . .
|