Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
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)

C++
1
2
3
4
5
ModuleDrive* dr = new ModuleDrive(); 
ModulePosition pos, pos2;
pos.data0 = 100;
pos2 = dr->MethodGet(pos);
dr->field = pos;
и вот цель шаблона прпоектирования - написать два унаследуемые класса, чтобы вот этот аналогичный код,
с заменой Module на Special - можно было использовать.

C++
1
2
3
4
5
SpecialDrive* dr = new SpecialDrive(); 
SpecialPosition pos, pos2;
pos.data0 = 100;
pos2 = dr->MethodGet(pos);
dr->field = pos;
---------------------------------------------------------------------------------------------------------------------

1) почему не годится - шаблон обычного наследования - и чего здесь не хватает для достижения данной цели,

В проекте ModuleProject (классы ModulePosition+ModuleDrive) - допустим, определены так -

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
class ModulePosition
{
    __int64 data0;
};
 
 
 
class ModuleDrive
{
    public:
    ModulePosition field; 
 
    ModulePosition  MethodGet(ModulePosition element)
    {
        field.data0 = element.data0; 
        //  + некий код,  нам неважно 
        return  obj;
    }
} ;
А теперь "ПРОСТО УНАСЛЕДУЕМСЯ" (обычный шаблон проектирования - наследование) ,
В проекте SpecialProject


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
class SpecialPosition : ModulePosition 
{
    //__int64 data0;   //  <---  унаследуемое от типа ModulePosition 
    int  data1;            //  + добавили что то новое 
};
 
 
 
class SpecialDrive : ModuleDrive
{
    public:
    SpecialPosition field; 
 
    SpecialPosition  MethodGet(SpecialPosition element)
    {
        base.MethodGet ((ModulePosition )element) ;   //  Важный момент.  Это нужно чтобы не дублировать код! 
        //  + добавили что то новое  
        return  obj;
    }
} ;
Всё вроде бы хорошо. и внешний код должен работать?
Нет!

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) почему не годится - шаблон обычных генерик-классов - и чего здесь не хватает для достижения данной цели.

Но если не наследоваться, а просто объявить (псевдокод) генерик-классы,

C++
1
2
class ModuleDrive <T>   : T is  ModulePosition  {...}
class SpecialDrive <T>   : T is  SpecialPosition  {...}
то действительно, поле field - в первом классе станет типа ModulePosition , а во втором - типом SpecialPosition ,
на уровне компилятора, что нам и нужно.
Метод MethodGet - имеет разные сигнатуры, а потому переопределяться не будет как виртуальный.
Следует ли из этого, что нам и вовсе не надо наследоваться, а достаточно использовать
ПРОСТО и только шаблон обычных генерик-классов ?

Здесь плохо вот что - в методе MethodGet - придется дублировать код из базового класса!
А дублирование кода - это ОЧЕНЬ НЕХОРОШО. (и Фаулер о том же писал).
В случае каких-то изменений, нужно помнить все места, где его нужно изменить.

Следовательно, было бы хорошо, если строку выше приведенную " Это нужно чтобы не дублировать код!",


C++
1
2
3
4
5
6
7
    
    SpecialPosition  MethodGet(SpecialPosition element)
    {
        base.MethodGet ((ModulePosition )element) ;   //  Важный момент.  Это нужно чтобы не дублировать код! 
        //  + добавили что то новое  
        return  obj;
    }

можно было использовать. Вывод - и ТОЛЬКО шаблон обычных генерик-классов без наследования -
нам не годится.

---------------------------------------------------------------------------------------------------------------------

Новый шаблон проектирования: Многоклассовое генерик-наследование.

Применительно к примерам, приведенным выше, это означает, что и

1) class SpecialDrive - должен быть НАСЛЕДНИКОМ ModuleDrive,

2) и к тому же, должны быть использованы генерик-классы, т.е. поле field; - у первого распознавалось
компилятром как SpecialPosition , а у второго как ModulePosition .


Как этого добиться? На C++ это можно сделать, потому что присутствует слово typedef -
означает что мы определяем новый тип, который эквивалентен данному, а не унаследован от него.

Мы объявим шаблонный класс,

C++
1
template< class Type > class TemplateDrive
и (внимание!) - класс ModuleDrive - мы не унаследуем от этого темплейта, а просто "приравняем" к нему,

C++
1
typedef TemplateDrive<ModulePosition> ModuleDrive;
т.е. для удобства - исользование названия ModuleDrive - будет эквивалентно использованию
громоздкого TemplateDrive<ModulePosition>.
Второй же класс, мы унаследуем

C++
1
class SpecialDrive : TemplateDrive<SpecialPosition>
и мы добились поставленной цели!
В самом деле,

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

типа,

C#
1
using DWORD = System.UInt64;
или как в нашем случае потребуется, что то типа (псевдокод),

C#
1
using ModuleDrive = TemplateDrive<T> : T  is  ModulePosition;
Но ВСЁ РАВНО ЭТО НЕ ТО.
Этот using - нам придётся вставлять в каждый файл с исходным кодом!
А если файлов много, + самих переопределяемых классов будет много - будет просто замусоривание кода.

---------------------------------------------------------------------------------------------------------------------

Зачем разработчики C#, отказались от использования слова typedef , позволяющее в одном месте
один раз объявить эквивалентный тип, и чтобы весь исходный код мог использовать его
непонятно, но преимущества typedef ,
я надеюсь, показал пунктуально.. и доступно. Нужно просто внимательно прочитать.

Итак, вот примерно такой код на C++ - и реализует данный шаблон проектирования.




C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//-------------ModulePosition  + SpecialPosition--------------------------------------------
 
 
 
 
struct ModulePosition
{
    __int64 data0;
};
 
 
 
struct SpecialPosition : ModulePosition
{
public:
    __int64 data2;
};
 
 
       //  объявлены как структуры, но сути шаблона проектирования это не меняет, аналогично было бы и с class
 
 
//------------------ModuleDrive + SpecialDrive--------------------------------------------
 
 
template< class Type >
class TemplateDrive
{   
public:
    Type field;
    Type MethodGet(Type element);
};
 
 
typedef TemplateDrive<ModulePosition> ModuleDrive;
 
 
 
// далее использование  ModuleDrive - ЭКВИВАЛЕНТНО TemplateDrive<ModulePosition>,  
// а потому класс, использующий именно ModulePosition, мы можем дописать как  ModuleDrive. 
// Попытка использовать с другим типом  TemplateDrive<ДругойТип>  провалится потому что в шаблонном class TemplateDrive - 
// нет реализации метода для всех других типов. По сути, конструкция выше эквивалентна конструкции C# 
// public partial class TemplateDrive<T> where T : ModulePosition
// и даже лучше т.к. в C# нельзя использовать везде без ограничений using ModuleDrive = TemplateDrive<ModulePosition>;  
// так, как в C++  typedef.  (using для этих целей можно использовать только в начале namespace).  
// Необъявление же реализации метода MethodGet - в классе TemplateDrive - так же решается и в C#, только с объявлением  abstract. 
// Поля в ModuleDrive объявляются закомментированными для удобства так же как и в C++, остаётся несущественная разница - 
// т.к. в C++ нет partial слова, то приходится комментировать и сам класс ModuleDrive, оставляя только реализацию методов. 
 
 
 
//class ModuleDrive   //  фактически - реализация класса будет здесь, но из-за отсутствия 'partial' приходится часть - оставлять в одном месте. 
//{
//public:
    //ModulePosition field;    //  Type field;  превращается в ModulePosition, т.к. в typedef объявлен тип 
 
 
    // здесь слово virtual повторно не нужно, т.к.  метод определен внутри класса TemplateDrive, видимо модификаторы virtual можно об-ть только внутри класса.
    // Если этот метод объявить как virtual в классе TemplateDrive тогда придется полностью переписывать код, а так - 
    // можно что то дописать в производных классах,  а часть оставить здесь и попадать сюда, в код базового класса тоже. 
    ModulePosition ModuleDrive::MethodGet(ModulePosition element)      // сигнатура та же, но Type -> превращается в ModulePosition
    {
        element.data0 = 11;   // "Точка1"
        field.data0 = 11;
        return element;
    }
//}    //  class ModuleDrive  
 
 
 
 
// Linker error LNK1120: 1 unresolved externals 
// TemplateDrive<SpecialPosition>* td2 = new TemplateDrive<SpecialPosition>();
// чтобы не было этой ошибки, нужно определить и в этом классе MethodGet, точнее - вызвать базовый 
    SpecialPosition TemplateDrive<SpecialPosition>::MethodGet(SpecialPosition element)      // сигнатура та же, но Type -> превращается в ModulePosition
    {
        //element.data0 = 11;
        //field.data0 = 11;
        //return element;
 
        ModulePosition el =  ((ModuleDrive*)this)->MethodGet((ModulePosition)element);  // "Точка2"
        element.data0 = el.data0;
        return element;
    }
 
 
 
// класс SpecialDrive, в отличие от ModuleDrive уже не объявляется через typedef, а прямо наследуется от TemplateDrive<SpecialPosition>. 
// Поскольку ModuleDrive  в свою очередь, не наследовался в другую ветку, а объявлялся прямо, то 
// 1) TemplateDrive<SpecialPosition>  ------> объект  наследник    от  ModuleDrive  (эквивалентен ему и  TemplateDrive<ModulePosition>)
//     поскольку тип в шаблоне - наследник. (SpecialPosition).  См.. свойство КОВАРИАНТНОСТИ. 
//     Содержит новое только в SpecialPosition внутреннем типе 
// 2) SpecialDrive - наследник от  TemplateDrive<SpecialPosition>, и следовательно от ModuleDrive. (содержит что то свое новое)
//     это значит что если сравнить SpecialDrive и ModuleDrive - то внутри и тип полей которые были ModulePosition- заменится на производные, 
//     и сигнатуры методов использующие этот тип,   и к тому же, сами методы можно переопределить,  + создать новые. 
// МНОГОКЛАССОВОЕ генерик-наследование наследование в этом и состоит. - 
//  Ниже, попадем в  "Точка3"   "Точка2"    "Точка1"  - последовательно, используя SpecialDrive  объект 
 
 
class SpecialDrive : TemplateDrive<SpecialPosition>
{
public:
    //SpecialPosition field;    //  Type field;  здесь превращается в SpecialPosition, т.к. тип объявлен у класса предка 
    int field2;   //  новое поле 
 
 
    // сигнатура та же, но Type -> превращается в SpecialPosition  - компилятор не ругается! 
    SpecialPosition MethodGet(SpecialPosition element) 
    {
        
        //element = TemplateDrive<SpecialPosition>::MethodGet(element); 
        element = ((TemplateDrive<SpecialPosition>*)this)->MethodGet(element);  // "Точка3"
 
        // доопределяем своё
        element.data2 = 22;
        field.data0 = 22;
 
        return element;
    }
};
 
 
 
void   OnBnClickedOk()
{
    
    ModuleDrive md1;
    TemplateDrive<SpecialPosition> td1;
    SpecialDrive sd1;
 
    // выше видно, что от чего наследуется,  ModuleDrive   ->  TemplateDrive<SpecialPosition>   ->  SpecialDrive
    //Watch:
    //в md1 видим что поле field имеет тип  ModulePosition
    //в td1 видим что поле field имеет тип  SpecialPosition
    //в sd1 - еще появилось поле field2
 
 
 
 
    ModuleDrive* m1 = new ModuleDrive(); 
    ModulePosition posM;
    posM.data0 = 100;
    m1->MethodGet(posM);   // 
 
 
    //  ЭКВИВАЛЕНТНЫЙ КОД, с использованием  производного целого ПРОЕКТА 
    
    SpecialDrive* m3 = new SpecialDrive();
    SpecialPosition posS;
    posS.data0 = 100;
    m3->MethodGet(posS);   //  Здесь, попадем в  "Точка3"   "Точка2"    "Точка1"  - последовательно, используя SpecialDrive  объект 
 
 
    // таким образом SpecialDrive НЕЯВНО унаследован от ModuleDrive,  одновременно с внутренними полями и сигнатурами методов, 
    // у SpecialDrive  внутри везде используется SpecialPosition,  а у ModuleDrive  - используется ModulePosition.  
 
    // Получается многоклассвое наследование.  Если далее создать еще пару классов,  к примеру 
    // ModuleController  и   SpecialController (3-й уровень),  второй так же неявно унаследовать от первого, а внутренние классы у них будут 
    // использоваться, вышеописанные  ModuleDrive и SpecialDrive (2-й уровень)  соответственно, то получим уже связное множество из 3-х классов, 
    // унаследованное от связного множества из  3-х классов. 
    // Продолжая по аналогии,  можно создать целый проект из N классов, который можно использовать как базовый для 
    // другого проекта, из унаследованных этих N классов. 
    
 
}
    
//----------------------------------------------------------------------------------------------------

Итак, вот примерно такой код на C++ - и реализует данный шаблон проектирования.

И хотелось бы знать, как его наиболее красиво было бы реализовать на C#.

Проще говоря, если мы используем обычное наследование классов, к примеру
InheritClass : BaseClass
то в вызываемом коде, все названия названия одного класса , BaseClass заменяются на InheritClass .

Что получается при соблюдении Многоклассового генерик-наследования. как будто,
целое связное множество классов, наследуется от другого связного множества
(псевдокод) -

InheritSet {InheritClass1, InheritClass2, InheritClass3 ...} : BaseSet {BaseClass1, BaseClass2, BaseClass3 ...}

и здесь наследование именно такое, что в Тьюринг-полном языке, любой код можно по аналогии, превратить
в рабочий код с другим проектом, просто заменив все названия из первого множества - их прототипами
из второго множества.

Новый шаблон проектирования: Многоклассовое генерик-наследование -
можно назвать также, "ПРОЕКТНОЕ наследование", когда целый проект с N классами, наследуется от другого
проекта, с N классами-прототипами.

Или, объединяя,

Новый шаблон проектирования: Проектное Многоклассовое генерик-наследование.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.03.2018, 17:38
Ответы с готовыми решениями:

Шаблон проектирования Prototype. Можно ли по такой реализации сказать, что используется этот шаблон?
public interface ICloneable&lt;T&gt; { T Clone(); } public class Client : ICloneable&lt;Client&gt; { ...

Шаблон проектирования State - нужен пример
Подкиньте пожалуйста ссылок с примерами реализации шаблона проектирования State, кроме википедии. Нужно для реализации задачи: Есть...

Шаблон проектирования MVP и несколько форм
Здравствуйте уважаемые форумчане. Начал рассматривать шаблон проектирования MVP(Model-View-Presenter). Пока в приложении только одна...

7
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) вместе с тем, есть НАСЛЕДОВАНИЕ

т.е. решена задача -

Применительно к примерам, приведенным выше, это означает, что и

1) class SpecialDrive - должен быть НАСЛЕДНИКОМ ModuleDrive,

2) и к тому же, должны быть использованы генерик-классы, т.е. поле field; - у первого распознавалось
компилятром как SpecialPosition , а у второго как ModulePosition .


Как этого добиться? На C++ это можно сделать, потому что присутствует слово typedef -
означает что мы определяем новый тип, который эквивалентен данному, а не унаследован от него.
Добавлено через 17 минут
Ну вроде, подробно всё объяснил как мог. Если совсем по простому - то тогда такой вопрос, ИНТЕРЕСНО , а вот почему в C++ есть такое удивительное слово typedef, а в C# его нет? Кажется что всё возможные "причины" отсутствия его - легко преодолеваются разработчиками C# и .NET, а вот новые возможности - появляются.

Добавлено через 16 минут
Появилась некая критика.

так можно ничего и не переименовывать, просто переписать код классов, и подсунуть новую библиотеку - и все заработает
можно. А цель - имея единый код, работающий с базовой библиотекой, и после написания новой библиотеки, не меняя этот внешний код, (либо максимум - меняя везде ОДНО СЛОВО в нём), и перекомплируя - получить возможность, чтобы он заработал с любой будущей "производной" - унаследуемой библиотекой. Разве не красиво ?

в плане "Очень сложных проектов", включающих в себя 100 библиотек, с разными зависимостями между собой, подобный подход может оказаться очень продуктивным и хорошим.

ООП -тоже изобретали, только потому чтобы люди могли написать более сложные проекты, а ЕСЛИ БЫ НЕ ЭТО (и были бы вопросы типа - "на хрена это всё". можно же и без этого обойтись) - то в принципе достаточно было бы ограничиться старым-добрым "Процедурным программированием". В плане теоретически-возможной функциональности - вполне достаточно.

Короче, я сам столкнулся с тем, что я вижу как можно упростить разработку и использование унаследуемых проектов.
"Зачем" - этот сложный вопрос можно пока оставить.

Другой вопрос - "зачем нет typedef в C#" , позволяющий всё упрощать - вот это интересно.
0
 Аватар для Fleder
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
22.03.2018, 08:46
SergeyYN, как же много букафф! А-а-а!

Цитата Сообщение от SergeyYN Посмотреть сообщение
Новый шаблон проектирования: Многоклассовое генерик-наследование.
Ух ты, круто! К каким шаблонам его причислим? Грасповским? Гофовским? Корпоративным? К авторским запатентованным?!

Цитата Сообщение от SergeyYN Посмотреть сообщение
Другой вопрос - "зачем нет typedef в C#" , позволяющий всё упрощать - вот это интересно.
А отсутствие макросов #define в C# вас не смущает? Ведь можно упростить всё ещё круче.

Цитата Сообщение от SergeyYN Посмотреть сообщение
Зачем разработчики C#, отказались от использования слова typedef , позволяющее в одном месте
один раз объявить эквивалентный тип
Разработчики предоставили вместо этого интерфейсы. Один раз определил - и реализуй сколько хочешь и какими угодно классами или структурами. И в каких угодно сборках.

Цитата Сообщение от SergeyYN Посмотреть сообщение
А теперь "ПРОСТО УНАСЛЕДУЕМСЯ" (обычный шаблон проектирования - наследование)
Лично моё мнение, не в обиду: наследование - это ни один из принципов ООП, как нам его преподносят, а лишь техническое средство достижения полиморфизма на уровне языка.
Наследование - это зло.
Вместо наследования надо использовать генерализацию.
Мыслить не сверху вниз по иерархии классов, а снизу вверх.
0
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
22.03.2018, 10:03  [ТС]
Цитата Сообщение от Fleder Посмотреть сообщение
Разработчики предоставили вместо этого интерфейсы.
И тем не менее, есть код на C++, который не переводится на C#, иначе чем с одной из четырёх проблем.

Цитата Сообщение от Fleder Посмотреть сообщение
К каким шаблонам его причислим?
Всё когда-нибудь делается в первый раз. Так и любой шаблон изобретается когда люди видят проблему
без его использования.

Цитата Сообщение от Fleder Посмотреть сообщение
А отсутствие макросов #define в C# вас не смущает?
define - более сложная вещь, имеющая свои последствия, речь не об этом.

А typedef - это всего лишь объявление эквивалентного типа.
Тут прозвучала критика, а что будет типа если typedef объявлен в одной библиотеке классов,
а потом кто то его переобъявит в другой библиотеке.


Не вижу проблемы здесь. Если ваш проект объявлен с reference на другой проект (одна dll будет использовать
типы другой dll), то тогда переобъявить тип с помощью typedef и таким же названием нельзя.
Компилятор не даст. Других понятных причин почему нет в C# typedef , пока не видно.
0
 Аватар для Fleder
263 / 224 / 108
Регистрация: 09.12.2015
Сообщений: 652
22.03.2018, 10:40
SergeyYN, а в чём проблема-то?
Зачем в C# нужен typedef?
Чтобы было проще портировать плюсовый код в шарповский?

Если же речь про разработку на шарпе с нуля, то typedef вообще не нужен.
Что мне мешает написать сборку (dll), в которой абсолютно все нестатические классы будут internal,
а публичными сделать только интерфейсы, простые структуры, перечисления и делегаты?

И если в будущем мне приспичит отнаследовать внутренние классы своей сборки от каких-то других,
то пользователь моей либы даже не заметит изменений, потому как классы мои ему недоступны,
а интерфейс останется прежний.

Если же я изменю и интерфейс тоже, то пользователю ничего не останется, кроме как, обматерив меня,
переписывать свой код.
0
Эксперт .NET
 Аватар для Rius
13151 / 7709 / 1679
Регистрация: 25.05.2015
Сообщений: 23,496
Записей в блоге: 14
22.03.2018, 10:52
Цитата Сообщение от SergeyYN Посмотреть сообщение
Других понятных причин почему нет в C# typedef , пока не видно.
Вы мыслите на C/C++. Здесь это не нужно. Хотите писать на C/C++ - пишите на нём.
А для переобъявления - упрощения достаточно алиасов.
1
83 / 1 / 0
Регистрация: 08.11.2017
Сообщений: 146
22.03.2018, 17:01  [ТС]
Цитата Сообщение от Fleder Посмотреть сообщение
Чтобы было проще портировать плюсовый код в шарповский?
Это тоже неплохо, но главное не это, а 1) устранение проблем со сложностью разработки, если даже на C# писать
с нуля.

Цитата Сообщение от Fleder Посмотреть сообщение
а публичными сделать только интерфейсы, простые структуры, перечисления и делегаты?
Хорошо, вот я ниже привожу код на C++, который реализует данный паттерн. Там по сути, как вы и сказали -
публичные только интерфейсы, и простые структуры, как вы и предложили.

Вот если кто то попробует, (по факту) - перевести код ниже на C# - тогда и поймет в чём проблемы
возникают.

Код -

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//--------------- {1}  PROJECT ModuleProject  -------------------------------------------------> 
 
 
class ModulePosition
{
    public: int data1 = 5;  // 5 - по умолчанию
    // для простоты - только один int, а вообще могут быть любые сложные данные. 
};
 
 
//  "Хитрая конструкция".  Зачем нужна -  описание ниже.
template< class Type >
class TemplateDrive
{
    protected:
 
    Type field1;
    Type field2;
    Type field3;
    Type field4;
    Type field5;   // для простоты, создадим 5 элементов, а вообще может быть список и т.д. что угодно - сложнее  
 
    public:
    void MethodSet(int seed);
    int MethodCalculate(Type element);
};
typedef TemplateDrive<ModulePosition> ModuleDrive;
// Конец "Хитрой конструкции". 
 
 
 
// в С++  нет ключевого слова 'partial', потому объявление класса ниже, ModuleDrive - закомментировано. Но по сути, это программистом воспринимается как 
// полноценно описанный класс, ModuleDrive - который НЕ УНАСЛЕДОВАН от шаблонного класса выше, 
// а именно - ЭКВИВАЛЕНТЕН ему (потому выше и использовали typedef и "Хитрую конструкцию" выше). 
// Итак, всё что есть в этом классе - или имплементировано, или закомментировано (но оно тоже ПРИСУТСТВУЕТ) -- 
 
 
//-------------------
//class ModuleDrive
//{
// protected:
  
// ModulePosition field1;
// ModulePosition field2;
// ModulePosition field3;
// ModulePosition field4;
// ModulePosition field5;  // все  поля  компилятор превратит здесь в   ModulePosition, 
 
//  public:
 
void ModuleDrive::MethodSet(int seed)
{
    // "Точка 1a"
    field1.data1 = 1 + seed;
    //  тут может быть любая сложная логика, но для простоты, написали некую простую реализацию в зависимости от параметра seed
}
 
 
int ModuleDrive::MethodCalculate(ModulePosition element)  // сигнатура похожая, но параметр Type -> превращается в ModulePosition. Всё обеспечивает "Хитрая конструкция" выше. 
{
    // "Точка 1b"
    int sum = 0;
    sum += field1.data1;
 
    return (sum * element.data1);
}
//}    //  class ModuleDrive    //  конец описания класса. И заодно, проекта ModuleProject.   Он простой, всего из двух классов. 
//-------------------
 
 
 
 
 
 
 
//--------------- {2} внешний код, использующий  PROJECT ModuleProject  ---------------------------------------> 
 
 
//  по сути, здесь эквивалент обращения только к методам интерфейсов. Ну, у ModulePosition поле data1 объявлено для простоты, 
// публичным.  Можно и его спрятать, и обращаться к нему через методы - суть от этого не изменится. 
 
/*
ModulePosition elem;
ModuleDrive* dr = new ModuleDrive();
dr->MethodSet(10);
int result = dr->MethodCalculate(elem);
*/
 
 
 
 
 
//--------------- {3}  внешний код, использующий будущий наследуемый PROJECT SpecialProject  -------------------------------------------------> 
 
// теперь нам нужно унаследовать цельный проект из N связных классов - другим проектом из N связных классов. 
// Многоклассовое генерик-наследование . Как будто, целое связное множество классов, наследуется от другого связного множества
// (псевдокод) -
// InheritSet{ InheritClass1, InheritClass2, InheritClass3 ... } : BaseSet{ BaseClass1, BaseClass2, BaseClass3 ... }
// и здесь наследование именно такое, что в Тьюринг-полном языке, любой код можно по аналогии, превратить
// в рабочий код с другим проектом, просто заменив все названия из первого множества - их прототипами
// из второго множества.
 
// Выше, в проекте ModuleProject использовалось для простоты только два класса, значит их и унаследуем. Т.е. 
// SpecialSet{ SpecialPosition, SpecialDrive ... } : ModuleSet{ ModulePosition, ModuleDrive ... }
// Внешний код должен быть только таким, и никаким другим !!!!! 
 
// !!!!!!!!!!!! 
/*
SpecialPosition elem;
SpecialDrive* dr = new SpecialDrive();
dr->MethodSet(10);
int result = dr->MethodCalculate(elem);
*/
 
// Никаких других, громоздких TemplateDrive<SpecialPosition>  или еще чего то - во внешнем коде быть НЕ ДОЛЖНО. 
// ООП -  изобретали, только потому чтобы люди могли написать более сложные проекты, а ЕСЛИ БЫ НЕ ЭТО 
// (и были бы вопросы типа - "а зачем это всё". можно же и без этого обойтись) - то в принципе достаточно было бы 
// ограничиться старым-добрым "Процедурным программированием". В плане теоретически-возможной функциональности - вполне достаточно. 
// Так и здесь - мы придерживаемся строгих наименований, потому что  - 
// в плане "Очень сложных проектов", включающих в себя 100 библиотек, с разными зависимостями между собой, подобный подход может 
// оказаться очень продуктивным и хорошим. после написания новой библиотеки, максимум что можно изменить во внешнем коде -  ОДНО СЛОВО в нём,
// В данном случае - заменили Module  на  Special - как базовое слово в названии библиотеки. 
// Итак, цель выше поставлена - именно такой внешний код, должен работать. 
 
 
 
 
//--------------- {4}  PROJECT SpecialProject  -------------------------------------------------> 
 
 
 
class SpecialPosition : public ModulePosition
{
public:
    //  int data1;   //  это поле, унаследовалось от ModulePosition
    int dataNew = 15;       //  + добавили что-то новое    (15 - по умолчанию)
};
 
 
 
 
class SpecialDrive : public TemplateDrive<SpecialPosition> // : ModuleDrive  (т.е. далее не пишем, но знаем, что в конечном итоге унаследуется от ModuleDrive ) 
{
    private:
 
    //  и 1++++ не надо создавать новые поля, явно указанные типом SpecialPosition, и впустую тратить память на данное поле - благодаря применению Генерик-классов!  
    // SpecialPosition field1;
    // SpecialPosition field2;
    // SpecialPosition field3;
    // SpecialPosition field4;
    // SpecialPosition field5;    //   унаследуемые поля  компилятор превратит здесь в   SpecialPosition, 
                
    int fieldNew;   // + добавили что то  новое  
 
 
    public:
 
    void MethodSet(int seed)
    {
        //  и  3++++ дублирования кода нет, благодаря наследованию. (если бы применили только Генерик классы, без наследования, то код пришлось бы определить в 2-х местах). 
        // аналог  шарповского  base.MethodSet(seed);
        ((ModuleDrive*)this)->MethodSet(seed);   // "Точка 2a"  --> попадем внутрь к точке 1a 
            
 
        // доопределяем новое 
        //  и  2++++ не надо делать явное преобразование типов каждый раз так ((SpecialPosition)field1).dataNew - благодаря применению Генерик-классов!  
        field1.dataNew = 1 + (seed * 2);
        field2.dataNew = 2 + (seed * 2);
        field3.dataNew = 3 + (seed * 2);
        field4.dataNew = 4 + (seed * 2);
        field5.dataNew = 5 + (seed * 2);
    }
 
 
    
    // сигнатура похожая, но   параметр ModulePosition -> превращается в SpecialPosition  
    int MethodCalculate(SpecialPosition element)
    {
        //  и  3++++ дублирования кода нет, благодаря наследованию. (если бы применили только Генерик классы, без наследования, то код пришлось бы определить в 2-х местах). 
        // аналог  шарповского  base.MethodCalculate(seed);
        int sum = ((ModuleDrive*)this)->MethodCalculate(element);   // "Точка 2b"  --> попадем внутрь к точке 1b 
 
        // + доопределяем новое 
        
        int sum2 = 0;
        sum2 += field1.dataNew;
        sum2 += field2.dataNew;
        sum2 += field3.dataNew;
        return (20* sum / sum2  + element.data1);
        
        return sum;
    }
    
};
 
 
 
//--------------- {5} ВЫВОДЫ   -------------------------------------------------> 
/*
Выше описаны два проекта, ModuleProject  и  SpecialProject ,  которые построены таким образом, что 
a) классы в них используют шаблоны (генерик-паттерн), 
b) и вместе с тем, классы второго проекта, наследуют классы первого проекта. 
 
c) внешний код который должен работать с первым проектом - 
 
ModulePosition elem;
ModuleDrive* dr = new ModuleDrive();
dr->MethodSet(10);
int result = dr->MethodCalculate(elem);
 
d) внешний код который должен работать с вторым проектом - 
 
SpecialPosition elem;
SpecialDrive* dr = new SpecialDrive();
dr->MethodSet(10);
int result = dr->MethodCalculate(elem);
 
 
 
!!!  
При попытке перевести этот  код   на 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 и позволяет. 
 
 
*/


При попытке перевести этот код на 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 минут
Цитата Сообщение от Rius Посмотреть сообщение
А для переобъявления - упрощения достаточно алиасов.
Вот я и хочу понять, почему алиас в файле объявить можно, а в библиотеке - нельзя.
(или всё таки как то можно?)
По сути typedef - это был бы аналог алиаса, только объявленный в сборке, и все библиотеки, которые будут
использовать эту dll, будут видеть два аналога объявления типа.
не надо заставлять программистов, что то типа (псевдокод)

C#
1
using ModuleDrive = TemplateDrive<T> : T  is  ModulePosition;
писать в каждом cs-файле кода.
Иделогогия .NET от этого кажется, не пострадала бы.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
22.03.2018, 17:01
Помогаю со студенческими работами здесь

Шаблон проектирования.Делфи
Здравствуйте.Очень нужна ваша помощь.Необходимо реализовать программу с шаблоном проектирования.Шаблон можно использовать любой.и задача на...

Шаблон проектирования JQuery
JQuery реализует шаблон проектирования MVC?

шаблон проектирования java
Какой шаблон в данной задаче лучше использовать? Создать классы, спецификации которых приведены ниже. Определить конструктор и методы...

Можно ли применить шаблон проектирования?
Есть 3 класса. Класс 2 создержит массив из объектов класса 1. Класс 3 содержит массив из объектов класса 2. Т.е. сплошная...

Шаблон проектирования для Зоопарка
Привет всем, помогите реализовать такую задачу: Для лучшего результата необходимо использовать шаблоны проектирования читал о таких...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Символические и жёсткие ссылки в 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
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru