Здравствуйте, VladD2, Вы писали:
VD>Тебе кажется.
Нет, это тебе кажется, что мне кажется..
VD>Контракты и интерефейсы тоже догмы той идиологии которую ты считашь единственно верной.
Контракты != Интерфейсы, вот интерфейсы это лишь один из способов явно оговорить контракты в ООП, понятие жэе контракта много шире и рамками ООП не ограничивается.
Поэтому вылезай из догмы о том, что я ограничен рамками ООП и ничего кроме этой идеологии не вижу. =)
VD> Если нет, то будет однобокое мышление.
Я тебя умоляю, мышление только мое не трожь..
VD>И что забавно очень часто совершенно напрасно.
Нет. Если мы решаем задачу через ООП, то совершенно не напрасно, если мы решаем ее как-то по другому, то причем здесь ООП?
Инкапсуляция один из фундаментальных принципов ООП, если он не нравится — решай задачу по другому, но ООП здесь не виновато..
VD> Проектировать в ОО-стиле все подряд не эффективно. ООП не панацея.
С этим никто не спорит. Вопрос в другом — ограничивается ли принцип Лисков ООП или его можно трактовать и применять несколько шире. Так вот мое мнение заключается в том, что таки да, принцип Лисков выходит за рамки ООП и является очень хорошим критерием правильности дизайна вне зависимости от используемого подхода (в озвученной выше формулировке, с которой и начался наш разговор)
VD> Он рассчитан на динамический полиморфизм в ООП.
Абстрагируйся от ООП и полиморфизма, взгляни шире на проблему. Есть контракт, есть реализация, есть код, который использует эту реализацию. Все, больше ничего нет, но эти понятия гораздо шире чем ОО и прочий полиморфизм, и вполне применимы и в твоей любимой функциональщине...
VD> Меж тем бывает статический полиморфизм в функциональном стиле. Он не менее мощен и позволяет решать ряд задач существенно проще чем в ОО-стиле.
Да байта ради, контракт от этого никуда не девается — как только один код начинает использовать результат работы другого кода появляется такая сущность как контракт, пусть это хоть процедурный язык будет.
VD>При том, что для фукнций данные не более чем то чеми они оперируют. Как int или double.
И что?
VD>В общем, этот принцип применим к узкому подтипу полиморфизма.
У меня такое чувство, что ты принцип ты для себя усвоил на основе примера, а не на основе того как он сформулирован.
Здравствуйте, IT, Вы писали:
IT>Не могу не согласиться с данным утверждением. В качестве примера можно упомянуть излишнюю многословность контрактов и их, мягко говоря, негибкость при дальнейшем развитии системы.
Могу только повторить, что контракты != интерфейсы, интерфейс — лишь способ явно описать контракт в ООП, со своими достоинствами и недостатками, можно пользоваться, можно не пользоваться — но сам контракт от этого никуда не денется...
Здравствуйте, IDL, Вы писали:
IDL>А можно по-подробнее о каких альтернативах идёт речь?
Есть несколько парадигм программирования каждая из них подразумевает некую идеологию проектирования. Забавно то, что их можно спокойно совмещать.
Так мы можем описывать задачу в объектах. Тогда разумно говорить о иерархии интерфесов и иерархии реализации. В этих рамках догмы ООП прекрасно работают. (ообектноориентированное программирование, ООП)
Так же мы можем описывать задачу как набор функцкий (возможно полиморфных) оперирующих с некоторыми (обычно очень простыми) структурами. (функциональное программирование, ФП)
Так же можно описать решение задачи в виде трансформации некой модели в некий код. (мета-программирование, МП)
Еще мы можем рассматривать задачу как набор логических условий (логическое программироание).
В зависимости от задачи эффективным становится тот или иной подход.
Забавно, что в области то где ООП по идее должен быть очень силен — построении моделей — он может уступить ФП. Например, при создании компилятора надо построить модель кода (так называемое абстрактное синтаксическое дерево). Если описывать каждый элемент такой модели как отдельный класс то получается огромное количество кода. Причем 90% этого кода (а то и больше) получается написанным под копирку. Плюс появляется проблема добавления стандартных методов обрабтки этой модели. Ведь любой вид обработки приводит к необходимости добавления новых виртуальных методов к каждому классу модели! Альтернативный вариант — реализовать паттерн Посетитель. Но и этот вариант создает проблем не намного меньше чем их мешает. Учитывая то что моделей в компиляторе бывает несколько (список токенов, нетипизированное АСТ, типизированное АСТ, промежуточный код...) объем компилятора пухнет на глахах.
Что же может предложить ФП в такой ситуации?
Два решения:
1. Использовать алгеброические типы вместо классов.
2. Отказаться от ООП подхода в обработке АСТ и воспользоваться для этого отдельными полиморфными функциями.
На этом месте имеет смысл остановить чтение моего сообщения и пойти прочитать статью Nemerle
(особенно ее резделы про сопоставление с образцом и варианты).
Так вот алгеброические типы (variant-ы в терминах Немерле) позволяют описать замкнутый набор типов. Например, для простого АСТ (арифмитических выражений) он может выглядеть так:
public variant Expr
{
| Literal { value : double; }
| Call { name : string; parms : list[Expr]; }
| Plus { expr1 : Expr; expr2 : Expr; }
| Minus { expr1 : Expr; expr2 : Expr; }
| Mul { expr1 : Expr; expr2 : Expr; }
| Div { expr1 : Expr; expr2 : Expr; }
}
Чтобы теперь написать преобразование выражения в строку мне достаточно объявить одну функцию в которой воспользоваться паттерн-матчингом:
public override ToString() : string
{
match (this)
{
| Literal(value) => value.ToString()
| Call(name, parms) => $"$name($(parms.Map(_.ToString()).ToString(\", \")))"
| Plus(e1, e2) with op = "+" | Minus(e1, e2) with op = "-"
| Mul(e1, e2) with op = "*" | Div(e1, e2) with op = "/" => $"$e1 $op $e2"
}
}
В общем, чтобы написать калькулятор на Немерле с использованием паттерн-матчинга и алгеброических типов у меня ушло 10 минут. Вот полный его код:
using System;
using System.Console;
using Nemerle.Utility;
using Expr;
public variant Expr
{
| Literal { value : double; }
| Call { name : string; parms : list[Expr]; }
| Plus { expr1 : Expr; expr2 : Expr; }
| Minus { expr1 : Expr; expr2 : Expr; }
| Mul { expr1 : Expr; expr2 : Expr; }
| Div { expr1 : Expr; expr2 : Expr; }
public override ToString() : string
{
match (this)
{
| Literal(value) => value.ToString()
| Call(name, parms) => $"$name($(parms.Map(_.ToString()).ToString(\", \")))"
| Plus(e1, e2) with op = "+" | Minus(e1, e2) with op = "-"
| Mul(e1, e2) with op = "*" | Div(e1, e2) with op = "/" => $"$e1 $op $e2"
}
}
public Eval() : double
{
match (this)
{
| Literal(value) => value
| Call("max", [arg1, arg2]) => Math.Max(arg1.Eval(), arg2.Eval())
| Call("min", [arg1, arg2]) => Math.Min(arg1.Eval(), arg2.Eval())
| Call(_, _) => throw InvalidOperationException(this.ToString());
| Plus(e1, e2) => e1.Eval() + e2.Eval()
| Minus(e1, e2) => e1.Eval() - e2.Eval()
| Mul(e1, e2) => e1.Eval() * e2.Eval()
| Div(e1, e2) => e1.Eval() / e2.Eval()
}
}
}
module Program
{
Main() : void
{
def expr = Plus(Literal(1.23), Call("max", [Literal(1), Literal(2)]));
WriteLine($"Expression '$expr' = $(expr.Eval())");
//_ = ReadLine();
}
}
Кстати, код был написан с использованием интеграции. Она малось глюкала, но таки сильно помогла.
Ради хохмы я реализовал аналогичный код на C#. Этот код написан в соотвествии с принципами проектирования на ООЯ (с использованием паттернов и наследования). Сравни объем, простоту и легкость расширения этого кода с кодом на Немерле.
using System;
using System.Collections.Generic;
using System.Text;
public interface IExprVisiter
{
void Visit(Literal expr);
void Visit(Call expr);
void Visit(Plus expr);
void Visit(Minus expr);
void Visit(Mul expr);
void Visit(Div expr);
}
public abstract class Expr
{
public abstract void AceptVisiter(IExprVisiter visiter);
public override string ToString()
{
ToStringExprVisiter visiter = new ToStringExprVisiter();
AceptVisiter(visiter);
return visiter.ToString();
}
}
public class Literal : Expr
{
public Literal(double value)
{
_value = value;
}
double _value;
public double Value
{
get { return _value; }
}
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
public class Call : Expr
{
public Call(string name, params Expr[] parm)
{
_name = name;
_parms = parm;
}
string _name;
public string Name
{
get { return _name; }
}
Expr[] _parms;
public Expr[] Parms
{
get { return _parms; }
}
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
public abstract class Operator : Expr
{
public Operator(Expr expr1, Expr expr2)
{
_expr1 = expr1;
_expr2 = expr2;
}
Expr _expr1;
public Expr Expr1
{
get { return _expr1; }
set { _expr1 = value; }
}
Expr _expr2;
public Expr Expr2
{
get { return _expr2; }
set { _expr2 = value; }
}
}
public class Plus : Operator
{
public Plus(Expr expr1, Expr expr2) : base(expr1, expr2) { }
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
public class Minus : Operator
{
public Minus(Expr expr1, Expr expr2) : base(expr1, expr2) { }
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
public class Mul : Operator
{
public Mul(Expr expr1, Expr expr2) : base(expr1, expr2) { }
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
public class Div : Operator
{
public Div(Expr expr1, Expr expr2) : base(expr1, expr2) { }
public override void AceptVisiter(IExprVisiter visiter)
{
visiter.Visit(this);
}
}
class ToStringExprVisiter : IExprVisiter
{
StringBuilder _bilder = new StringBuilder();
public override string ToString()
{
return _bilder.ToString();
}
void PrintList(IList<Expr> lst)
{
foreach (Expr var in lst)
{
var.AceptVisiter(this);
_bilder.Append(", ");
}
if (lst.Count > 0)
_bilder.Length -= ", ".Length;
}
public void Visit(Literal expr)
{
_bilder.Append(expr.Value);
}
public void Visit(Call expr)
{
_bilder.Append(expr.Name);
_bilder.Append("(");
PrintList(expr.Parms);
_bilder.Append(")");
}
void VisitOperator(Operator expr, string kind)
{
expr.Expr1.AceptVisiter(this);
_bilder.Append(kind);
expr.Expr2.AceptVisiter(this);
}
public void Visit(Plus expr)
{
VisitOperator(expr, " + ");
}
public void Visit(Minus expr)
{
VisitOperator(expr, " - ");
}
public void Visit(Mul expr)
{
VisitOperator(expr, " * ");
}
public void Visit(Div expr)
{
VisitOperator(expr, " / ");
}
}
class EvalExprVisiter : IExprVisiter
{
public double Result;
public double Eval(Expr expression)
{
expression.AceptVisiter(this);
return Result;
}
public void Visit(Literal expr)
{
Result = expr.Value;
}
public void Visit(Call expr)
{
switch (expr.Name)
{
case"min":
if (expr.Parms.Length != 2)
throw new InvalidOperationException(expr.ToString());
Result = Math.Min(Eval(expr.Parms[0]), Eval(expr.Parms[1]));
return;
case"max":
if (expr.Parms.Length != 2)
throw new InvalidOperationException(expr.ToString());
Result = Math.Max(Eval(expr.Parms[0]), Eval(expr.Parms[1]));
return;
default: throw new InvalidOperationException(expr.ToString());
}
}
public void Visit(Plus expr)
{
Result = Eval(expr.Expr1) + Eval(expr.Expr2);
}
public void Visit(Minus expr)
{
Result = Eval(expr.Expr1) - Eval(expr.Expr2);
}
public void Visit(Mul expr)
{
Result = Eval(expr.Expr1) * Eval(expr.Expr2);
}
public void Visit(Div expr)
{
Result = Eval(expr.Expr1) / Eval(expr.Expr2);
}
}
class Program
{
static void Main(string[] args)
{
Expr expr = new Plus(new Literal(1.23), new Call("max", new Literal(1), new Literal(2)));
Console.WriteLine("Expression '{0}' = {1}", expr, new EvalExprVisiter().Eval(expr));
}
}
Мне кажется результат сам говорит за себя. Нарушение догм ООП, а точнее просто выбор другого подхода существенно (более чем в пять раз, 50 строк на Немерле против 265 строк на C#) упростил реализацию.
Но возможно мы получим проблемы в дальнейшем, например, при развитии нашего калькулятора? Ведь именно это нам предрекают те кто считают ОО-догмы незыблемыми. Но это не так. Напротив мы получили контроль компилятора и простоту расширения функциональности.
Например, если мы захотим добавить поддержку вычисления остатка от деления (аналогично оператору % в C), и добавим в вариант еще одно вхождение "Mod":
public variant Expr
{
...
| Div { expr1 : Expr; expr2 : Expr; }
| Mod { expr1 : Expr; expr2 : Expr; }
...
то при компиляции, компилятор честно предупредит нас о том, что новое вхождение варианта необработано:
Main.n(18,5,18,10): warning : matching is not exhaustive, example unmatched value: Mod
Main.n(29,5,29,10): warning : matching is not exhaustive, example unmatched value: Mod
Нам остается перейти в те места куда указывает компилятор и добавить соотвествующие обработчики. Это опять же выльется всего в две строки кода.
На C# мы будем вынуждены:
1. Добавить новый класс.
2. Изменить интерфейс посетителя.
3. Изменить все реализации посетителя добавив к ним новый метод.
А теперь внимание вопрос. А нафига нужно следовать догмам если в конкретном случае они вредят делу?
Предвижу замечание сведующего народа. "Но ведь ты использовал более мощьный язык..." Да, это так. Но сути дела это не меняет. Догмы от этого догмами быть не перестают. И было бы очень полезно, чтобы программист даже ограниченный конкретным ЯП знал о "других путях". Это знание позволит ему более здраво смотреть на решаемые задачи. А возможно поможет упростить решаемую задачу заменой инструмента на более подходящих.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IB, Вы писали:
IT>>Не могу не согласиться с данным утверждением. В качестве примера можно упомянуть излишнюю многословность контрактов и их, мягко говоря, негибкость при дальнейшем развитии системы. IB>Могу только повторить, что контракты != интерфейсы, интерфейс — лишь способ явно описать контракт в ООП, со своими достоинствами и недостатками, можно пользоваться, можно не пользоваться — но сам контракт от этого никуда не денется...
Естественно контракты != интерфейсы. Контракты более широкое понятие. Скажем так, интерфейс — это контракт внутри одной платформы. В свою очередь контракт — это межплатформенный интерфейс. Но суть у них одна и та же. Проблемы тоже.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IDL, Вы писали:
IT>>Не могу не согласиться с данным утверждением. В качестве примера можно упомянуть излишнюю многословность контрактов и их, мягко говоря, негибкость при дальнейшем развитии системы.
IDL>А можно по-подробнее о каких альтернативах идёт речь?
Влад уже показал некоторые альтернативы.
Конкретно по контрактам можно сказать, что они очень даже имеют смысл когда речь идёт о взаимодействии с внешними независимыми системами. Т.е. мы не знаем кто и когда нас будет пользовать, но мы очень хотим, чтобы это произошло. На практике таких задач, где контракты реально нужны, а не высосаны из пальца очень и очень не много. Писать внутренности системы с использованием контрактов, конечно, никто не запрещает, но это дорого и практически не имеет никого смысла, т.к. преимущества подхода становятся несущественны, а недостатками можно наслаждаться в полной мере.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT> Контракты более широкое понятие.
Угу..
IT>Скажем так, интерфейс — это контракт внутри одной платформы.
Не, интерфейс это именно способ явно описать контракт в рамках ООП и не более того.
А то о чем говорит Влад — полиморфизм, наследование, ect.. — это лишь способ подменить реализацию интерфейса (контракта), и в этом смысле ООП лишь предоставляет несколько больше возможностей прострелить себе ногу подменив реализацию, но новых сущностей не вводит...
IT>В свою очередь контракт — это межплатформенный интерфейс.
Да не межплатформенный.. Ппонятие контракта возникает везде, где один код пытается использовать результат работы другого кода. Просто когда платформа одна то сломав контракт, обычно можно походя поменять или реализацию, или спользующий код, что несколько сглаживает проблему, на первый взгляд, но радости от этого не много...
IT>Но суть у них одна и та же. Проблемы тоже.
По поводу проблем, мы с тобой в марте за бутылочкой кетел вана поговорим, в уютном месте, все равно в сиэтл через NY лететь..
Вопрос явного описания контракта — это вопрос воздействия на вкусовые рецепторы на предмет сравнительного анализа на сладость хрена и редьки.. =))
Здравствуйте, IB, Вы писали:
VD>>Контракты и интерефейсы тоже догмы той идиологии которую ты считашь единственно верной. IB>Контракты != Интерфейсы,
Да, бога ради. Я даже не собирался касаться отношений контракта и интерфейса. Ты что-то слишьно зациклился на этих контрактах. Вот тебя и тянет о них спорить.
Первична задача. То как решить ее наиболее просто и эффективно. А уж чем ты там будешь оперировать... контрактами, интерфейсами или фунциями и структурами мне по барабану.
IB>вот интерфейсы это лишь один из способов явно оговорить контракты в ООП, понятие жэе контракта много шире и рамками ООП не ограничивается.
Ваань. К чему ты все это говоришь? Ты не забыл о том, что обсуждалось?
IB>Поэтому вылезай из догмы о том, что я ограничен рамками ООП и ничего кроме этой идеологии не вижу. =)
Это реальность. То что ты этог даже не замечашь проблемы твои, а не мои.
VD>> Если нет, то будет однобокое мышление. IB>Я тебя умоляю, мышление только мое не трожь..
Вань. Ты влез в спор чтобы сказать свое окончательное верное слово? Или чтобы перевести разговор в плоскость обсуждения контрактов? Я этим уже наигрался. Мне это сейчас не интересно. Поробуй понять то что тебе говорят.
VD>>И что забавно очень часто совершенно напрасно. IB>Нет. Если мы решаем задачу через ООП, то совершенно не напрасно, если мы решаем ее как-то по другому, то причем здесь ООП?
Мы осбуждали незыблемость приципа/догмы. Я сказал, что он выглядит натянуто если расширить свой кругозор и рассматривать не только ООП, а и другие подходы тоже. Принцип ведь распространяется на типы как таковые и нигде не сказано, что он действует в рамках иерахических типов с динамическим полиморфизмом.
IB>Инкапсуляция один из фундаментальных принципов ООП, если он не нравится — решай задачу по другому, но ООП здесь не виновато..
Она доведена до догмы. А все что превращено в догму рано или поздно становится тормозом в развитии. Не все проблемы в программировании надо решать с помощью ООП. ООП не всегда эффективен. Пример тому я привел в соседней ветке
.
VD>> Проектировать в ОО-стиле все подряд не эффективно. ООП не панацея. IB>С этим никто не спорит. Вопрос в другом — ограничивается ли принцип Лисков ООП или его можно трактовать и применять несколько шире. Так вот мое мнение заключается в том, что таки да, принцип Лисков выходит за рамки ООП и является очень хорошим критерием правильности дизайна вне зависимости от используемого подхода (в озвученной выше формулировке, с которой и начался наш разговор)
Ты таки заблуждаешся. Он не имеет смысла за пределами номинальной системы типов (иерархической с динамическим полиморфизмом). Есть системы типов без наследования, без динамического полиморфизма, и даже без инкапсуляции (точнее с ограниченной инкапсуляцией). И как не странно такие "странные" системы типов могут оказваться более эффектиными в некоторых случаях. Пример ты можешь наблюдать выше по ссылке.
VD>> Он рассчитан на динамический полиморфизм в ООП. IB>Абстрагируйся от ООП и полиморфизма, взгляни шире на проблему. Есть контракт, есть реализация, есть код, который использует эту реализацию. Все, больше ничего нет, но эти понятия гораздо шире чем ОО и прочий полиморфизм, и вполне применимы и в твоей любимой функциональщине...
Вань. Перестань повторять базворды. Забуть ты про контракты и реализации. Пойми, есть задача. И надо найти самый оптимальный путь ее решения. Будешь ли ты оперировать базвордами вроде "контракт", "интерфейс", "наследование"... или "функция высшего порядка", "алгеброический тип", "преобразование функций", "трансформация данных"... совершенно не важно. Важно только то насклько простое, расширяемое решение и поддерживаемое решение ты получишь в итоге. А базворды — это всего лишь средство выглядить умно ничего не зная. Ты явно в таких средствах не нуждаешся. Так что брось ты их.
VD>> Меж тем бывает статический полиморфизм в функциональном стиле. Он не менее мощен и позволяет решать ряд задач существенно проще чем в ОО-стиле. IB>Да байта ради, контракт от этого никуда не девается — как только один код начинает использовать результат работы другого кода появляется такая сущность как контракт, пусть это хоть процедурный язык будет.
Нет не то что бы контракта. Нет нужды думать в пдобных понятиях. Точнее конечно нужда может быть, а может и не быть. Еще точнее можут оказться так, что рассуждать других терминах окажется выгоднее. Понимаешь о чем я?
VD>>При том, что для фукнций данные не более чем то чеми они оперируют. Как int или double. IB>И что?
Ну, и то, что вместо рассуждения "объект", "онтракт"... мы выбераем рассуждения "данные", "трансформация", ... а про контракты просто не думать. И тогда вдруг может оказаться, что решение нарушающее какие-то там незыблемые принципы оказывается более выгодным и простым. И все потому, что ты подругому взглянул на проблему.
VD>>В общем, этот принцип применим к узкому подтипу полиморфизма. IB>У меня такое чувство, что ты принцип ты для себя усвоил на основе примера, а не на основе того как он сформулирован.
А у тебя никогда не было чувства, что ты просто не понимаешь или не знаешь чего-то?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Да, бога ради. Я даже не собирался касаться отношений контракта и интерфейса.
Почему же ты тогда о них заговорил?
VD> А уж чем ты там будешь оперировать... контрактами, интерфейсами или фунциями и структурами мне по барабану.
Ну, в данном-то случае речь об оценке этой операции.. )
VD>Ваань. К чему ты все это говоришь?
К тому что ты упомянул интерфейсы..
VD> Ты не забыл о том, что обсуждалось?
Обсуждался LSP, в котором ты нашел некие "догмы" ООП... Ты сам-то помнишь о чем речь?
VD>Это реальность.
В каком-то измененном сознании..
VD>Поробуй понять то что тебе говорят.
Вот, это ты хорошо сказал. Влад, попробуй понять, что тебе говорят.
VD>Мы осбуждали незыблемость приципа/догмы.
Во-первых "догма" — это уже твоя демагогия, границы применимости так же звучат гораздо лучше чем незыблемость — это во вторых.
VD> Я сказал, что он выглядит натянуто если расширить свой кругозор и рассматривать не только ООП, а и другие подходы тоже.
Я же сказал, что этот принцип совершенно не зависит от подхода, если забыть про ООП и расширить его на контракты и реализации.
VD> Принцип ведь распространяется на типы как таковые и нигде не сказано, что он действует в рамках иерахических типов с динамическим полиморфизмом.
Нет. Принцип вообще ни как не завязан на типы.
VD> Не все проблемы в программировании надо решать с помощью ООП.
Тебе еще раз меня процитировать? "Никто с этим ни спорит, если ты решаешь задачу посредством ООП — не жалуйся на инкапсуляцию — не доволен инкапсуляцией — решай задачу по другому — ООп здесь не виновато."
Я и в третий раз могу, если что.. =)
VD>Ты таки заблуждаешся.
Таки нет..
VD> Он не имеет смысла за пределами номинальной системы типов (иерархической с динамическим полиморфизмом).
Он вообще никак не завязан на типы. Ну взгляни ты на проблему шире..
VD> Есть системы типов без наследования, без динамического полиморфизма, и даже без инкапсуляции (точнее с ограниченной инкапсуляцией). И как не странно такие "странные" системы типов могут оказваться более эффектиными в некоторых случаях. Пример ты можешь наблюдать выше по ссылке.
Тебе еще раз повторить, что типы здесь непричем? Наследование, в купе с полиморфизмом, лишь способ подменить реализацию контракта, но само понятие контракта ООП не ограничивается.
VD>Вань. Перестань повторять базворды.
Я их повторяю?
VD> Забуть ты про контракты и реализации.
Это ты лучше про них вспомни..
VD> Важно только то насклько простое, расширяемое решение и поддерживаемое решение ты получишь в итоге.
Совершенно нечего возразить.. =) В данном же случае мы обсуждаем критерий по на основе которого можно заключить, на сколько расширяемое и поддерживаемое решение у нас получилось.
И LSP, в озвученной выше формулировке в качестве одного из таких критериев, меня вполне устраивает...
VD> Еще точнее можут оказться так, что рассуждать других терминах окажется выгоднее. Понимаешь о чем я?
Понимаю, боюсь только ты не понимаешь о чем я..
VD> И тогда вдруг может оказаться, что решение нарушающее какие-то там незыблемые принципы оказывается более выгодным и простым. И все потому, что ты подругому взглянул на проблему.
Во-первых, никто не говорил, что принцип незыблем, хочешь — нарушай... ) Во-вторых, если решение оказалось более простым но менее расширяемым и поддерживаемым, то пошло нафиг такое решение, этот код еще сопровождать надо, причем с хорошей вероятностью совсем не тому кто его писал. Ну и, наконец, в третих — если решение оказалось простым, выгодным и расширяемым, то оно, как правило, таки удовлетворяет вышеприведенному принципу, как на проблему ни гляди..
Здравствуйте, IT, Вы писали:
IT>Конкретно по контрактам можно сказать, что они очень даже имеют смысл когда речь идёт о взаимодействии с внешними независимыми системами...
А как насчет взаимодействия с другими подсистемами в большом проекте? Особенно, если разные подсистемы пишут разные люди? Мне кажется, здесь без контрактов будет проблематично. Какие есть варианты?
Здравствуйте, Дм.Григорьев, Вы писали:
IT>>Конкретно по контрактам можно сказать, что они очень даже имеют смысл когда речь идёт о взаимодействии с внешними независимыми системами...
ДГ>А как насчет взаимодействия с другими подсистемами в большом проекте? Особенно, если разные подсистемы пишут разные люди? Мне кажется, здесь без контрактов будет проблематично. Какие есть варианты?
Это зависит от конкретного большого проекта и его конкретных подсистем. Часто можно обойтись простыми лэйерами и модулями.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Неправильно выразился... LSP — это только критерий, которому должны удовлетворять подтипы, если мы хотим, чтобы название "подтип" было корректным. Но при этом возможна забавная коллизия, когда с точки зрения компилятора типы находятся в отношении "супертип-подтип", а с точки зрения LSP — нет.
Угу. Это потому, что наследование, как инструмент, может использоваться для разных целей. В результате "типы" для компилятора не соответсвуют один в один "типам" для программистов.
Здравствуйте, IT, Вы писали:
IT>Это зависит от конкретного большого проекта и его конкретных подсистем. Часто можно обойтись простыми лэйерами и модулями.
А между слоями и модулями контрактов нет?
Здравствуйте, IB, Вы писали:
IT>>Это зависит от конкретного большого проекта и его конкретных подсистем. Часто можно обойтись простыми лэйерами и модулями. IB>А между слоями и модулями контрактов нет?
В том смысле в котором они понимаются теми, кто ввёл этот термин — нет. Если твоя интерпретация уже практически не отличается от, например, "контракт есть публичный интерфейс класса/модуля и т.п.", то можно сказать, что у модулей контракт есть. Только нафига тогда нужен такой термин, без которого до этого прекрасно обходились и не было никакой путаницы.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>В том смысле в котором они понимаются теми, кто ввёл этот термин — нет. Если твоя интерпретация уже практически не отличается от, например, "контракт есть публичный интерфейс класса/модуля и т.п.", то можно сказать, что у модулей контракт есть. Только нафига тогда нужен такой термин, без которого до этого прекрасно обходились и не было никакой путаницы.
Как же? Бащврды шибко способствуют самовнушению.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>В общем, чтобы написать калькулятор на Немерле с использованием паттерн-матчинга и алгеброических типов у меня ушло 10 минут. Вот полный его код: VD>
VD>public variant Expr
VD>{
VD> | Literal { value : double; }
VD> | Call { name : string; parms : list[Expr]; }
VD> | Plus { expr1 : Expr; expr2 : Expr; }
VD> | Minus { expr1 : Expr; expr2 : Expr; }
VD> | Mul { expr1 : Expr; expr2 : Expr; }
VD> | Div { expr1 : Expr; expr2 : Expr; }
VD> public override ToString() : string
VD> {
VD> match (this)
VD> {
VD> | Literal(value) => value.ToString()
VD> | Call(name, parms) => $"$name($(parms.Map(_.ToString()).ToString(\", \")))"
VD> | Plus(e1, e2) with op = "+" | Minus(e1, e2) with op = "-"
VD> | Mul(e1, e2) with op = "*" | Div(e1, e2) with op = "/" => $"$e1 $op $e2"
VD> }
VD> }
VD>
До боли что-то напоминает... А! Это же старый добрый С! Уж не так ли мы писали в течение 20 лет:
struct expr
{
enum type {Literal, Call, Plus, Minus, Mul, Div} t;
union
{
double value;
struct {const char* name; expr* params[10]; int count;};
struct {expr *expr1, *expr2;};
} data;
};
void to_str(const expr* e)
{
switch (e->t)
{
case expr::Literal: cout << e->data.value; break;
case expr::Call: cout << e->data.name << "(";
for (int i = 0; i < e->data.count; ++i)
{
to_str(e->data.params[i]);
cout << "," + !!i;
}
cout << ")"; break;
case expr::Plus: to_str(e->data.expr1); cout << "+"; to_str(e->data.expr2); break;
case expr::Minus: to_str(e->data.expr1); cout << "-"; to_str(e->data.expr2); break;
case expr::Mul: to_str(e->data.expr1); cout << "*"; to_str(e->data.expr2); break;
case expr::Div: to_str(e->data.expr1); cout << "/"; to_str(e->data.expr2); break;
}
}
Имхо тождественно с точностью до синтаксиса. Практически так же лаконично.
И уж не от этого ли мы бежали? Уж не это ли у нас было плохо поддерживаемым, error-prone и т.д? И уж не глядя на это ли мы решили, что хорошо было бы иметь более структурированный код и более явно выделять сущности (пустай даже ценой менее лаконичного кода)?
Нет, я согласен, для этого конкретного примера код на C# выглядит как overkill. Но в начале поста пример-то у тебя был другой — несколько типов деревьев, куча типов нодов и т.д.
В С, кстати, "умные" компиляторы или тулзы тоже выдают варнинг на "неполные" switch'и. Но тем не менее такой подход мировым сообществом программистов был признан... не очень хорошим.
Твой "switch" выглядит красивым и лаконичным и читаемым пока он такой маленький и не распух. А представь у тебя будет на каждый case по 10-50 строк кода (разное поведение в зависимости от настроек, логирование и т.д. а в промышленном проекте так и будет). Что ты сделаешь? Правильно. Вынесешь код в функции. А вот тут уже станут сомнительны приемущества такого switch'а — уже сам этот switch будет пустой писаниной, т.к. vtable реализует его автоматически (0 строчек кода со стороны программиста). И уже пошли возможности для ошибок — а что если я и для Plus и для Minus вызову одну функцию?
Или вот ещё аналогичный пример. Допустим есть некая функция. Пока относительно небольшая, она выграет от того, что мы её не будем разделять на несколько — одна функция из 10 строк — просто, читаемо, можно охватить логику одним взглядом. Если же функцию из 10 строк разбить на 3, то во-первых, кол-во кода существенно вырастет, во-вторых станет менее читаемо, т.к. не так локально.
Когда же есть функция из 100 строк, то она уже будет проигрывать от того, что она реализована одной функцией на 7 экранов, т.к. она уже все равно не простая и не лаконичная и одним взглядом её не поймёшь. Тут она уже выграет от разбивания на несколько функций, т.к. код станет более структурированным, более чётко будут видны интерфейсы и зависимости, от некоторых частей можно будет абстрагироваться и т.д. При этом кол-во кода вырастет — но мы готовы с этим мириться, т.к. взамен получаем более важные вещи.
Так же и с ООП. Для небольших задач оно может показаться overkill, одна для больших и сложных задач лучше написать больше кода, но зато явно выделить все понятия, разложить всё по полочкам. Вобщем структурировать. Допустим есть базовый класс Expression c 5 виртуальными функциями и есть 147 производных классов. Что бы всё это понять достаточно изучить базовый класс и один наследник. Всё. далее, когда надо будет допустим изменить поведение "сложения" понятно, что надо найти некий ExpressionPlus и изменить его.
Вобщем, к чему я это всё. ООП предоставляет очень мощные инструменты для создания больших проектов. Возможно его зачастую не к месту используют для решения таких задач как калькулятор выше — это да, согласен. Но тем не мене такие понятия как абстракции, ортогональность — сила.
Проблема, что кода много и он "по копирке" существует. Но есть приёмы, которыми практически всегда можно свести кол-во "повторяющегося" кода к минимуму. Если, конечно, захотеть это сделать.
Здравствуйте, IT, Вы писали:
IT>В том смысле в котором они понимаются теми, кто ввёл этот термин — нет.
Разьве? Ну ладно, не суть...
IT> Только нафига тогда нужен такой термин, без которого до этого прекрасно обходились и не было никакой путаницы.
Ну хорошо, замени контракт на "публичный интерфейс модуля, ect.." суть-то от этого не изменится.
Здравствуйте, IT, Вы писали:
ГВ>>>>>>[...]Если один фрагмент — не беда, если десяток, то... IT>>>>>И в чём именно проблема? ГВ>>>>Только в количестве. IT>>>Ну так функционал всё равно писать надо, много его или мало. ГВ>>А кто-то это отрицает? IT>Так в чём тогда проблема?
В расширяемости, разумеется.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Вообще-то твое сообщение на 90% является офтопиком. Посему начну с конца... который не так далеко ушел от темы: R>Вобщем, к чему я это всё. ООП предоставляет очень мощные инструменты для создания больших проектов. Возможно его зачастую не к месту используют для решения таких задач как калькулятор выше — это да, согласен. Но тем не мене такие понятия как абстракции, ортогональность — сила.
Все что я хотел сказать когда вмешался в эту тему — это то, что есть разные подходы, разные взгляды на решение проблем (разные способы мышления). Причем удобны то одни, то другие... в завиимости от обстоятельств. И заблуждение многих заключается в том, что они проецируют правило выработанное в рамках одного подхода на все программирование в целом. И мне кажется я четко опказал, что это неверное обобщение.
Я не маньяк-функциональщик уверовший в оду религию, и ни в коем случае не хочу чтобы мои слова были поняты как обвинение ООП. Я всего лишь говорю, что ООП — это один из возможных путей решения проблем. Не лучший и не худший. У него есть свои приемущества и свои недостатки. И это важно понимать. А так же важно понимать, что многие постулаты ООП верны только в его рамках. Более того. ООП тоже бывает разными и постулаты вреные для одного ответвления могут легко оказаться бессмысленными для другого. Хорошими примерами являются прототипные языки (Питон и ему подобные).
Ну, а теперь по пунктам...
R>До боли что-то напоминает... А! Это же старый добрый С!
Скорее Лисп или ОКамл, но тебе явно они незнакомы, вот ты узрел тут С. С тем же успхом бвышие дельфийцы прейды на дотнет постоянно узнают в C#-е Обеъектный Паскаль.
R> Уж не так ли мы писали в течение 20 лет:
R>
R>Имхо тождественно с точностью до синтаксиса. Практически так же лаконично.
Серьезно? Так же? Сравни сам:
public override ToString() : string
{
match (this)
{
| Literal(value) => value.ToString()
| Call(name, parms) => $"$name($(parms.Map(_.ToString()).ToString(\", \")))"
| Plus(e1, e2) with op = "+" | Minus(e1, e2) with op = "-"
| Mul(e1, e2) with op = "*" | Div(e1, e2) with op = "/" => $"$e1 $op $e2"
}
}
И это самый простой случай. Чем более сложный будет код тем хуже он будет на С. Реализуй, плиз, весь пример на С и попробуем сравнить. Особенно интересует как ты повторишь вот эти строки:
Поясню, что в них особенного. Это как раз паттерн-матчинг во всей своей мощи. Мы очень короткими и хорошо читаемыми строками "Call("max", [arg1, arg2])" распознаем конкретные случае. Если такой возможности нет, то прийдется писать кучу занудных if-ов с кучей ручных проверок, каждая из которых черевата ошибками.
R>И уж не от этого ли мы бежали?
Вы? Может не надо обобщать?
R> Уж не это ли у нас было плохо поддерживаемым, error-prone и т.д?
Ага. Это незнаю только почему на английском. Твой код действительно подвержен ошибкам и плохо поддерживаем. Причем последнее является следствием первого, а не отсуствием ООП.
С и С++ — это опасные, а от того марально устаревшие языки. Провреку type приходится делать вручную и если ошибиться и не врено интерпретировать данные из union-а, то мы и получим те самые ошибки. Ну, и боязнь сама по себе приводит к трудности в расширении и т.п.
R> И уж не глядя на это ли мы решили, что хорошо было бы иметь более структурированный код и более явно выделять сущности (пустай даже ценой менее лаконичного кода)? R>
Ключевое слов здесь "мы". Эти "мы" слепо бегут за новшествами по пути охаивая все что было раньше. Но были и другие мы. Были те кто не забыл хорошего и не очень старого. Кто проанализировал в чем проблемы в этом старом и исправил их. Забавно, что даже про старое говорить то нельзя. Лисп старше чем С. Так что эти другие "мы" развивали другой взгляд.
Панацей небывает! Нет единого способа получить "более структурированный код и более явно выделять сущности". Нетути. Есть подходы способные добиться этого. И подходов не один. А раз так, то архи глупо догмами одного из подходов устранять возможность применения другого.
R>Нет, я согласен, для этого конкретного примера код на C# выглядит как overkill.
А раз так, то разговаривать в общем-то не очем. Ведь ты подтвердил мои слова. А раз так, до и правило автоматически становится догомой.
R> Но в начале поста пример-то у тебя был другой — несколько типов деревьев, куча типов нодов и т.д.
У меня? Пример был не мой. Он даже был не автора. Это стандартная калька из книг по ООЯ. Пример этот, кстати, в реальной жизни никогда не встречается, так как наследование для подобных задач тоже полнейший оврекил. Большинство библиотек 2D-графики обычно выводят геометрические примитивы отдельными функциями. О наследовнии там даже речи не идет. Ну, да это так отступление.
Так вот, пример был не у меня. И пример использовался чтобы доказать, что прицип верный. И я всего лишь показал, что примеры бвывают и другие. И показал, что в этих других примерах охаиваемый вариант не просто работает, а отлично работает и дает значительно более простое и легко-поддерживаемое решение.
R>В С, кстати, "умные" компиляторы или тулзы тоже выдают варнинг на "неполные" switch'и.
Кстатати, можно хотя бы один пример таких компиляторов?
И кстати, ты точно понимашь суть паттерн-матчинга? Ты помнимашь, что компилятор отслеживает не только примитивные случаи, но более сложные:
то компилятор опять таки выдаст предупреждение. Ни один компилятор С на такое не способен.
Ну, и нужно понимать, что и это примитивный случай. Паттерн-мтачинг может быть намного более сложным. Компилятор строит дерево логического вывода и стало быть контролирует весь процесс и паттерны любой сложности.
R> Но тем не менее такой подход мировым сообществом программистов был признан... не очень хорошим.
Серьезно? А мировое сообщество то и не знало.
Оно вот взяло и породило Лисп, МЛ, ОКамл и в итоге Немерле. Так что не надо выдвать ограниченность своего кругозора за решение мирового сообщества. И не надо противопоставлять ООП всему остальному. Он хорош для многих задач, но не для всех. Просто нужно уметь применять его с умом, а так же уметь применять и не его. Ну, и опять же хочу заметить, что надо остерегаться догм.
R>Твой "switch"
Мой свитч? Он свитч, только для тебя, так как ты просто незнаком с паттерн-матчингом и от того для тебя это эдакая разновидность свитча. Меж тем это занчительно более мощьная возможность.
R>выглядит красивым и лаконичным и читаемым пока он такой маленький и не распух. А представь у тебя будет на каждый case по 10-50 строк кода (разное поведение в зависимости от настроек, логирование и т.д. а в промышленном проекте так и будет).
Это огромное заблуждение. Если скажем увеличить количество сущьностей, например, расширить калькулятор до современного ЯП, то код выростет пропроционально. Так что если у нас исходно получилась разница в 5 раз, то она и останется. Так что если компилятор на Немерле занимает 1.5 мегабайта, то анологичный код на C# будет 7.5 мебагайт, а то и больше.
R> Что ты сделаешь? Правильно. Вынесешь код в функции.
Не факт. В функцию я буду выносить не всегда.
R> А вот тут уже станут сомнительны приемущества такого switch'а — уже сам этот switch будет пустой писаниной, т.к. vtable реализует его автоматически (0 строчек кода со стороны программиста). И уже пошли возможности для ошибок — а что если я и для Plus и для Minus вызову одну функцию?
Я не знаю как можно перепутать плюс и минус. Зато я знаю, что это не свитч. И знаю, что с помощью миртуальных методов реализовать функциональность паттерн-матчинга невозможно. Вот простой пример попавшийся под руку. Компилятор Немерле генерировал не очень красивый код для булевых выражений (правда опимизатор дотнета устраля проблему, но все же). Вот код птача исправляющего проблему:
Добавленный код помечен плюсиками. Обратите внимание на паттерн. Я конечно понимаю, понять его не зная зяыка и остобенностей устройства компилятора сложно. Но это и не важно. Важно то, что этот паттерн находит выражение "x == (true :> bool)" (или в теминах С "x == (bool)true"), где "x" это любое выражение, и генерирует для него более простой код. В результате компилятор порождает более простой код. Вместо:
Реализация того же самого на базе виртуальных методов невозможна в приципе. Вместо этого прийдется написать довольно объемный и плохо читаемый код испещренный if-ами.
Теперь перейдем к "функциям vs. вхождения матча". Тут прозвучала теория о том, что мол большие матчи нечитабельны и что мол это ухудшает поддержку.
Это неверная теория. У меня есть сразу несклько аргументов против нее.
1. В сущьности нет разницы между классом содержащим много функций кайждая из которых обрабатывает (скажем) одну ветку и одной функцией содержащей много образцов обрабатвающих одну (а то и не оду) ветку. Это высосанное из пальца предубеждение. Наборот охватить галзами компактный список проще. Тут мне могут заметить, что пробему создают распухший код между образцами (код обработчиков), но эту проблему легко решает среда с поддержкой свортывания код (фолдингом).
Например, следующий код:
GetExpr(expression : TExpr) : object
{
| StaticRef(from, mem, _) => // { from : MType.Class; mem : IMember; type_parms : list [TyVar]; }if (mem.MemberType != MemberTypes.Constructor && from.tycon.Name == PExprName)
from
else
mem : object
| TypeConversion(_, tt, _) => // { mutable expr : TExpr; target_type : TyVar; kind : ConversionKind; }if (tt.IsFixed)
{
match (tt.FixedValue)
{
| Class(tycon, _) =>
def dt = tycon.DeclaringType;
if (dt != null && dt.Name == PExprName) dt else tt : object
| _ => tt
}
}
else
tt
| Literal(Enum(value, ty, null)) =>
def members = ty.GetMembers();
def field = members.Find(f =>
{
match (f)
{
| f is IField when f.IsStatic && f.IsLiteral =>
match (f.GetValue())
{
| Integer as val
| Literal.Enum(val, _, _) => val.ToString() == value.ToString()
| _ => false
}
| _ => false
}
});
match (field)
{
| Some(f) => f
| None => expression : object
}
| Literal(Enum(_, _, field)) => field
| ConstantObjectRef(_, o : object) // { from : MType.Class; mem : IField; }
| StaticPropertyRef(_, o : object) // { from : MType.Class; prop : IProperty; }
| StaticEventRef (_, o : object) // { from : MType.Class; ev : IEvent; }
| Base (o : object) // { base_ctor : IMethod; }
| LocalFunRef (o : object, _) // { decl : LocalValue; type_parms : list [TyVar]; }
| LocalRef (o : object) // { decl : LocalValue; }
| MethodRef (_, o : object, _, _) // { obj : TExpr; meth : IMethod; type_parms : list [TyVar]; notvirtual : bool; }
| EventMember (_, o : object) // { obj : TExpr; ev : IEvent; }
| FieldMember (_, o : object) // { obj : TExpr; fld : IField; }
| PropertyMember (_, o : object) // { obj : TExpr; prop : IProperty; }
| DefValIn (o : object, _, _) // { name : LocalValue; val : TExpr; mutable body : TExpr; }
| HasType (_, o : object) // { expr : TExpr; test_ty : MType; }
| TypeOf (o : object) => o // { target_type : TyVar; }
| _ => expression
}
В IDE будет выглядеть вот так:
2. Реализация паттерн-матчинга в виде оператора — это одна из возможных реализаций. В Немерле в виду того, что язык имеет C-подобный (точнее C#-подобный) синтаксис проблематично реализовать другой варинт. Но есть ЯП в которых возможен паттерн-матчинг на базе отдельных фукнкций. Стало быть исчезает последний натянутый аргумент.
3. Паттерн-матчинг намного мощьнее просто виртуальных методов. Мы может не просто отбирать что-то по одному признаку. Мы можем строить очень сложные паттерны которые сравнимы с программой не малого размера. При этом паттерны практически не требуют отладки и легко проверяются компилятором и IDE. Пример я уже показывал выше.
В общем, апеляция к длинне явно некорректна. Это не более чем попытка найти пробелему там где ее нет, чтобы оправдать путь изобилующий реальными проблемами.
R>Или вот ещё аналогичный пример. Допустим есть некая функция. Пока относительно небольшая, она выграет от того, что мы её не будем разделять на несколько — одна функция из 10 строк — просто, читаемо, можно охватить логику одним взглядом. Если же функцию из 10 строк разбить на 3, то во-первых, кол-во кода существенно вырастет, во-вторых станет менее читаемо, т.к. не так локально. R>Когда же есть функция из 100 строк, то она уже будет проигрывать от того, что она реализована одной функцией на 7 экранов, т.к. она уже все равно не простая и не лаконичная и одним взглядом её не поймёшь. Тут она уже выграет от разбивания на несколько функций, т.к. код станет более структурированным, более чётко будут видны интерфейсы и зависимости, от некоторых частей можно будет абстрагироваться и т.д. При этом кол-во кода вырастет — но мы готовы с этим мириться, т.к. взамен получаем более важные вещи.
А ты рассматривай каждое вхождение матча как одельную фукнцию. Да и в чем собственно разница? Ну, да я уже повторясь.
R>Так же и с ООП. Для небольших задач оно может показаться overkill, одна для больших и сложных задач лучше написать больше кода, но зато явно выделить все понятия, разложить всё по полочкам.
Это сказка про белого бычка. ООП не более чем одно решение. Оно несомненно способствует построению больших приложений, так как ООП предоставляет нам правила декомпозиции задач. Онако подобные правила нам предоставляет и ФП (причем они другие), и даже обычное струкутрное программирование. Как не странно, но самые большие продукты до сих пор пишутся на С. Конечно по сравнению со структурным подход ООП дает больше возможностей для декомпозиции. Однако уже по сравнению с ФП подход ООП зачастую проигрывает. ФП позволяет производить декомпозцию функций, что в ООП затруднительно. В ФП есть методы более мощьные нежели принятые в императивном подходе который свойственнен и ООЯ.
Так что разумно сочетать разные подходы выбирая для решения конкретных подзадач тот подход который дает нибольшие приемущетсва в конкретном случае.
R> Вобщем структурировать. Допустим есть базовый класс Expression c 5 виртуальными функциями и есть 147 производных классов. Что бы всё это понять достаточно изучить базовый класс и один наследник. Всё. далее, когда надо будет допустим изменить поведение "сложения" понятно, что надо найти некий ExpressionPlus и изменить его.
Твои предположения о простоте не более чем теоритические рассуждения. А факты упрямая вещь. Функциональные языки намного лучше подходят для написания компиляторов. Чтобы в этом убедиться достаточно сравнить код двух компиляторов написанны на ООЯ и на ФЯ. Наприер, код компилятора Немерле занимает 1.5 МБ, а во много раз более простого Питона несколько десятков.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IB, Вы писали:
VD>>Да, бога ради. Я даже не собирался касаться отношений контракта и интерфейса. IB>Почему же ты тогда о них заговорил?
Ты почитай внимательно обсуждение. О них заговорил ты и ты же все время пытался подменить обсуждение конкретной роблемы обсуждением значимости контрактов в народом хозайстве.
VD>>Ваань. К чему ты все это говоришь? IB>К тому что ты упомянул интерфейсы..
Еще раз. Их упоминашь исключительно ты. Я говорил о том, что применение данного принципа становится бессмысленным как только ты отходишь от приципов ООП и пользуешся другими (возможно более эффективными) приципами. А стало быть говорить, о том, что этот принцип всегда верен нельзя.
IB>Обсуждался LSP, в котором ты нашел некие "догмы" ООП... Ты сам-то помнишь о чем речь?
Я? Прекрасно. Проблема как раз с твоим пониманием. И знаешь что? К сожалению, чтобы тебе меня понять тебе нужно кроме ООП изучить и другие подходы. Иначе разговор не получится.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.