Здравствуйте, igna, Вы писали:
I>Здравствуйте, Философ, Вы писали:
Ф>>с каких пор это стало антипаттерном?
I>Не стало, а всегда было. Твой MyMethod1 надо сделать интерфейсом
OK
interface IB
{
bool MyMethod1();
}
I> и добавить параметр типа этого интерфейса методу PublicMethod1.
с какой целью?
abstract class A<TIB> where TIB:IB
{
public int PublicMethod1<TIB> ()
{
//Зачем мне здесь понадобился TIB, что мне теперь с ним делать?
//...if (<????????????????????????>.MyMethod1())..
//....
}
}
Попробуем по другому...
abstract class A
{
public int PublicMethod1 (IB p_ib)
{
//...if (p_ib.MyMethod1())..
//....
}
}
Замечательно! Что получили?
Данные по прежнему остались в абстрактном классе A, из него вынесли один protected метод из N...
ОК, вынесли все N методов (вероятно, наплодив N интерфейсов).
Чем стало лучше?
I>Проще — значит лучше...
Не вижу здесь никакой простоты
I>а все что не по делу усложняет программу является антипаттерном.
Не согласен. Я вообще не знаю что такое антипаттерн. Если под словом паттерн понимать название общепринятого способа решать задачу (или способ решения, без имени), то антипаттерн — общепринятое отрицательное мнение о способе решения задачи.
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали: Ф>Да, ООП здесь можно заменить на что угодно. Ф>Незрелая область деятельности, авторы "учебников" выдают субъективное мнение за истину, хотя и пишут не о сути мироздания, а об интсрументах и способах решения задач, преподносят своё понимание хорошего и плохого.
Да вроде бы не то, чтобы незрелая. Просто городские легенды кочуют из одного учебника в другой. Все эти объекты "автомобиль" с наследниками "грузовой автомобиль" — полная ерунда, которая учит вредным идеям.
S>>Редкий частный случай, когда имеет смысл наследовать реализацию, а не интерфейс, выдаётся за основное применение. Ф>Не согласен. Наследование реализации способно во многих случаях сократить кол-во кода, дублирование, а следовательно и сложность.
С чем конкретно вы несогласны? Ф>Пример: представим класс A, для которого планируется сделать торчу наследников
Не вижу примера. Вижу применение паттерна "шаблонный метод", без комментариев о том, зачем, собственно, это нужно, и почему вы хотите именно наследоваться.
Понятно, что вы хотите дать возможность наследнику переопределять метод MyMethod(). Но непонятно, почему вы требуете для этого именно наследования.
Рассмотрим более реалистичный пример: коллекцию и метод для подсчёта её длины:
public abstract IEnumerator<T> GetEnumerator();
public int Count
{
get
{
var count = 0;
foreach(var item in this) count++;
return count;
}
}
Вот этот наивный подход требует от нас ажно наследоваться от специфического класса только ради того, чтобы повторно использовать метод подсчёта Count.
Как мы знаем, в реальных решениях применяют значительно более слабую связность — строят внешний extension method Count(), который работает с любым IEnumerable, а не только с тем, который нам предоставил наследник.
Вот и в вашем примере можно избавиться от избыточного требования наследоваться от вашего базового класса при помощи банальной рокировки:
interface IA
{
bool MyMethod1();
}
...
public static int PublicMethod1(this IA a)
{
//...if (a.MyMethod1())..
//....
}
Или вообще отказаться от статической типизации и требования реализации интерфейса, которое ограничивает применение в PublicMethod1 классов из посторонних библиотек:
public static int PublicMethod1(Func<bool> a)
{
//...if (a())..
//....
}
Видите, как резко расширяется сфера возможного применения вашего PublicMethod. Вы же хотели повторного использования? Ну так наследование максимально ограничивает его вохможности.
S>>В итоге мы имеем разрыв между тем, чему учат в учебниках и тем, что стоит применять на практике. Отсюда и весь этот горький катаклизм, который мы наблюдаем.
Ф>А наблюдаем мы на практике то, что было когда-то в учебнике
Не, на практике мы наблюдаем результаты неудачных попыток применять учебные примеры, разбившихся о рифы суровой реальности.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали: S>Здравствуйте, Философ, Вы писали: S>>>Редкий частный случай, когда имеет смысл наследовать реализацию, а не интерфейс, выдаётся за основное применение. Ф>>Не согласен. Наследование реализации способно во многих случаях сократить кол-во кода, дублирование, а следовательно и сложность. S>С чем конкретно вы несогласны?
С утверждением "смысл наследовать реализацию — редкий частный случай". Отнюдь не редкий. Ф>>Пример: представим класс A, для которого планируется сделать торчу наследников S>Не вижу примера. Вижу применение паттерна "шаблонный метод"
Посмотрел описание паттерна. Да это именно он и есть S>, без комментариев о том, зачем, собственно, это нужно, и почему вы хотите именно наследоваться.
Виноват.
Попробую исправится.
Собственно паттерн "шаблонный метод" был мною найден случайно, в процессе попыток упросить код и уменьшить дублирование. О паттернах проектирования я тогда только слышал.
!) Предпосылки:
interface IA
{
void PublicMethod1 ();
}
Классов, реализующих этот интерфейс планировалось великое множество, и было кое-что, что их объединяло:
а) одинаковые приватные поля
б) одинаковые реализации некоторых (!) приватных приватных методов
в) одинаковая реализация публичного метода
Иллюстрация:
Скрытый текст
//Реализация метода Method2() в этих классах отличается,
//но в рамках решаемой задачи присутствует он во всех классах с интерфейсом IAclass A1 : IA
{
private bool SomeType m_SomeField;
private bool Method1 ()
{
//Use m_SomeField
}
private bool Method2 ()
{
}
void PublicMethod1 ()
{
//Use Method1
}
}
class A2 : IA
{
private bool SomeType m_SomeField;
private bool Method1 ()
{
//Use m_SomeField
}
private bool Method2 ()
{
}
void PublicMethod1 ()
{
//Use Method1
}
}
class AN : IA
{
private bool SomeType m_SomeField;
private bool Method1 ()
{
//Use m_SomeField
}
private bool Method2 ()
{
}
void PublicMethod1 ()
{
//Use Method1
}
}
Таким образом интерфейс был заменён на абстрактный класс.
abstract class IA
{
protected abstract bool Method2 ();
public void PublicMethod1 ()
{
}
protected bool SomeType m_SomeField;
protected bool Method1 ()
{
//Use m_SomeField
}
}
S>Понятно, что вы хотите дать возможность наследнику переопределять метод MyMethod(). Но непонятно, почему вы требуете для этого именно наследования.
Наследование здесь позволило убрать дублирование кода.
S>Вы же хотели повторного использования?
Нет, я не хотел повторного использования. Целью было упрощение кода и исключение дублирования, повторное было побочным продуктом.
//Сорри, осилил не всё.
//Чуть позже продолжу.
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, Философ, Вы писали:
Ф>>Данные по прежнему остались в абстрактном классе A, из него вынесли один protected метод из N...
I>Так данные-то тоже вынести надо. В класс имплементирующий интерфейс.
Здравствуйте, Sinclair, Вы писали:
S>Ну то есть берётся какая-нибудь вполне разумная вещь, и начинает доводиться до абсурда. И этот абсурд каким-то непонятным мне образом проникает в популярную литературу.
... S>Очень мало литературы упирает на поведение в поисках иерархий наследования, и много — на состояние. Редкий частный случай, когда имеет смысл наследовать реализацию, а не интерфейс, выдаётся за основное применение.
"Абсурд" этот ни в какую литературу по ООП не проник, он в ней был с самого начала, по крайней мере со времени популяризации C++. Не надо теперь делать вид, будто все было бы хорошо, если бы не некие абсурдописатели, иначе к последним нужно отнести самого Страуструпа, унаследовал же он Circle от Shape на первых же страницах своей The C++ Programming Language. Далее, Гослинг при разработке Java выкинул множественное наследование, но оставил наследование реализации очевидно отнюдь не считая последнее "редким частным случаем".
Здравствуйте, igna, Вы писали:
I>"Абсурд" этот ни в какую литературу по ООП не проник, он в ней был с самого начала, по крайней мере со времени популяризации C++. Не надо теперь делать вид, будто все было бы хорошо, если бы не некие абсурдописатели, иначе к последним нужно отнести самого Страуструпа, унаследовал же он Circle от Shape на первых же страницах своей The C++ Programming Language.
Я не понял. Вы с кем не согласны — со мной или со Страуструпом?
Вы что, серъёзно полагаете, что наследование Circle от Shape — хорошая, годная идея, которая может реально применяться в какой-то программе?
I>Далее, Гослинг при разработке Java выкинул множественное наследование, но оставил наследование реализации очевидно отнюдь не считая последнее "редким частным случаем".
Хм. То есть вы полагаете, что из наличия наследования реализации в Java прямо следует то, что Гослинг не считал его редким частным случаем?
Очень интересно. Расскажите мне тогда, как он должен был поступить, если бы всё-таки считал его редким, но важным частным случаем.
И заодно расскажите, какие выводы мы можем сделать про различие подходов Гослинга и Страуструпа к различным видам наследования из наличия в Java интерфейсов и отсутствия оных в C++.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Философ, Вы писали:
Ф>С утверждением "смысл наследовать реализацию — редкий частный случай". Отнюдь не редкий.
Ф>
Ф>interface IA
Ф>{
Ф>void PublicMethod1 ();
Ф>}
Ф>
Ф>Классов, реализующих этот интерфейс планировалось великое множество, и было кое-что, что их объединяло: Ф>а) одинаковые приватные поля Ф>б) одинаковые реализации некоторых (!) приватных приватных методов Ф>в) одинаковая реализация публичного метода
Пока непонятно даже, что вас подвигло на порождение этого вашего интерфейса, т.е. чем вас не устроил делегат Action в качестве такого "интерфейса". Ф>Таким образом интерфейс был заменён на абстрактный класс.
По-прежнему непонятно, чем именно отличались все эти множественные классы, у которых одинаковые приватные члены.
Ф>Наследование здесь позволило убрать дублирование кода.
Какой именно код у вас дублировался?
Я тут уже давал ссылку на пример с TextReader.
Вот там наследование реализации уместно, т.к. наследник может выбирать, сколько методов перекрывать. Перекрыв метод побайтного чтения Read можно сразу получить работающую реализацию. Перекрытие методов ReadLine и ReadAlllines позволяет улучшить производительность наивного решения, если она не устраивает. При этом наследоваться от конкретного TextReader если хочется всего лишь немножко подправить его поведение категорически не надо — надо агрегироваться.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Я не понял. Вы с кем не согласны — со мной или со Страуструпом?
С обоими.
1. ООП, в том виде в котором его популяризировали, вещь вредная.
2. Утверждать теперь, что это, мол, и не ООП вовсе было, а так, заблуждение, а настоящее ООП вот оно, большое, розовое и пушистое; можно конечно, но на мой взгляд неприлично.
S>Вы что, серъёзно полагаете, что наследование Circle от Shape — хорошая, годная идея, которая может реально применяться в какой-то программе?
Нет, это негодная идея, если фигуры изменяемы, а в программировании в отличие от математики они как правило являются таковыми.
S>Очень интересно. Расскажите мне тогда, как он должен был поступить, если бы всё-таки считал его редким, но важным частным случаем.
Скорее всего выбросил бы как выбросил множественное наследование. Тоже ведь "редкий, но важный частный случай".
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Философ, Вы писали:
Ф>>С утверждением "смысл наследовать реализацию — редкий частный случай". Отнюдь не редкий. S>
Ф>>
Ф>>interface IA
Ф>>{
Ф>>void PublicMethod1 ();
Ф>>}
Ф>>
Ф>>Классов, реализующих этот интерфейс планировалось великое множество, и было кое-что, что их объединяло: Ф>>а) одинаковые приватные поля Ф>>б) одинаковые реализации некоторых (!) приватных приватных методов Ф>>в) одинаковая реализация публичного метода S>Пока непонятно даже, что вас подвигло на порождение этого вашего интерфейса, т.е. чем вас не устроил делегат Action в качестве такого "интерфейса".
Это стёб чтоль?
Считаете ли вы меня придурком?
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
Ф>Это стёб чтоль? Ф>Считаете ли вы меня придурком?
Гыгы. В общем, ИМХО твои оппоненты просто забыли произнести слово "SRP". Но вообще, правильное решение будет зависеть от конкретной ситуации. В большинстве случаев делегат лучше, а единственный более-менее часто испольуемый паттерн, когда рулит наследование, совсем недавно вот тут неподалёку
Здравствуйте, dimgel, Вы писали:
D>Здравствуйте, Философ, Вы писали:
Ф>>Это стёб чтоль? Ф>>Считаете ли вы меня придурком?
D>Гыгы. В общем, ИМХО твои оппоненты просто забыли произнести слово "SRP".
что не так здесь с SRP?
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
Ф>что не так здесь с SRP?
С SRP всё хорошо, спасибо. Просто применение этого принципа как правило даёт кучу простых взаимодействующих объектов, в классах которых наследование реализации почти никогда не требуется. А предпочтение наследования как правило приводит к злоупотреблению им, что на выходе даёт запутанные иерархии. Самый часто встречающийся в моей практике пример — когда вместо того, чтобы вынести некий повторно используемый код в какой-нибудь отдельный Util, делают общий базовый класс для совершенно разнородных вещей и забабахивают этот повторно используемый код в protected-метод базового класса. Вот до отвращения уже достали это говно рефакторить, ей богу.
Здравствуйте, dimgel, Вы писали:
D>Здравствуйте, Философ, Вы писали:
Ф>>что не так здесь с SRP?
D>С SRP всё хорошо, спасибо. Просто применение этого принципа как правило даёт кучу простых взаимодействующих объектов, в классах которых наследование реализации почти никогда не требуется. D>А предпочтение наследования как правило приводит к злоупотреблению им, что на выходе даёт запутанные иерархии. Самый часто встречающийся в моей практике пример — когда вместо того, чтобы вынести некий повторно используемый код в какой-нибудь отдельный Util, делают общий базовый класс для совершенно разнородных вещей и забабахивают этот повторно используемый код в protected-метод базового класса. Вот до отвращения уже достали это говно рефакторить, ей богу.
Я не вижу никакой связи между наследованием и нарушением SRP. Люди склонные к этому, прекрасно справляются и без наследования, и даже без ООП: например в модуле с файловыеми операциями вполне могут оказаться функции и глобальные переменные относящиеся к графике.
SRP хотя и формулировался для ООП вполне применим за его пределами
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
Ф>Я не вижу никакой связи между наследованием и нарушением SRP. Люди склонные к этому, прекрасно справляются и без наследования, и даже без ООП
Это понятно. "Все языки полны по Тьюрингу, поэтому на любом языке можно написать ужасный код." ~(c) VladD2. Но здесь обсуждалось, как надо лучше, а не хуже. А связь я в первой же строчке своего предыдущего сообщения указал.
Здравствуйте, dimgel, Вы писали:
D>Здравствуйте, Философ, Вы писали:
Ф>>Я не вижу никакой связи между наследованием и нарушением SRP.
D>А связь я в первой же строчке своего предыдущего сообщения указал.
D>А предпочтение наследования как правило приводит к злоупотреблению им, что на выходе даёт запутанные иерархии.
Это?
Вы мне мозг взрываете. Какое к чёрту "предпочтение"? Мы тут чем занимаемся поставленную задачу своим кодом решаем, или картину маслом пишем? Если второе, то я предпочитаю сочетание зелёного с ультрамарином.
Если же мы пишем код, то для класса не нарушающего SRP, одинаковые действия для всей иерархии, в рамках решаемой задачи, будут выделены в неприватные методы. Бывает, конечно совсем тяп-ляп: никаких отдельных методов создано не будет, а решение построено с помощью старинной китайской технологии "Copy-paste".
Всё сказанное выше — личное мнение, если не указано обратное.
Здравствуйте, Философ, Вы писали:
Ф>>>Я не вижу никакой связи между наследованием и нарушением SRP. D>>А связь я в первой же строчке своего предыдущего сообщения указал.
Ф> D>>А предпочтение наследования как правило приводит к злоупотреблению им, что на выходе даёт запутанные иерархии. Ф> Ф>Это?
Нет, вот это: "Просто применение этого принципа как правило даёт кучу простых взаимодействующих объектов, в классах которых наследование реализации почти никогда не требуется."
Ф>Вы мне мозг взрываете. Какое к чёрту "предпочтение"?
Любую более-менее сложную задачу можно решить огромным множеством разных способов. Выбор решения — это предпочтение и есть.