Re[7]: Кто автор?
От: stalcer Россия  
Дата: 03.08.05 06:21
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>1) Абстрактый Тип Данных (АТД) определяется только набором операций над ним, и никоим образом не атрибутами.


Аттрибуты — это операции чтения/записи, также со своими предусловиями и постусловиями.

G>При этом, у АТД все равно есть множество возможных значений, и множество значений подтипа является подмножеством множества значений типа. Вот это, последнее — единственная общая черта, и это необходимое, но совсем не достаточное условие.


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

G>2) А вот это серьезное отличие: у наших объектов есть состояние и модифицирующие операции (нонсенс с точки зрения математики — она вообще под таким углом на проблему не смотрит), и модифицирующие операции у нас являются замкнутыми, т.е. не могут выводить объект из его типа.


У понятий предметной области в общем случае все равно есть поведение. И здесь у меня волникают две мысли:

1) Я могу согласится, что операций изменения в матетатике нет. Ну, дык тогда и причина противоречия налицо: мы вводим в программу операции, которых в предметной области вообще нет. Естественно это может нарушить изначальное отношение обего и частного.
2) Доказывать какую-нибудь теорему я вполне могу начать со слов: Возмем квадрат S с размером стороны 10. И это будет аналогично программе Sqare s = new Square(10). Разве нет? Так что манипулирование объектами все равно есть.

G>Причина нарушения ППЛ в нашем случае состоит именно в том, что операции изменения размеров не являются замкнутыми — они выводят квадрат из класса квадратов.


Вай-вай, как нехорошо. Вот же мое решение
Автор: stalcer
Дата: 01.08.05
(см. код).

G>А именно, из того факта, что одно множесто является подмножеством другого, вовсе не следует, что они связаны отношением подтипа.


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

G>Как заканчивались наставления самураев по владению техникой меча, это следует внимательно изучить.


Да изучали уже вдоль и поперек . Просто хочется понять вот что: может быть LSP, это не тот идеал, к которому следует стремиться, может быть он именно такой за неимением лучшего решения? Именно из-за недостаточно глубокого понимания основ окружающего нас мира.
http://www.lmdinnovative.com (LMD Design Pack)
Re[18]: Кто автор?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 03.08.05 08:00
Оценка:
Здравствуйте, gbear, Вы писали:

G>1. Наличие Inscribed Circle у прямоугольника возможно только в четко определенном его инварианте. Соответсвенно, если агрегат требует наличия Inscribed Circle (а он именно требует, т.к. работает с прямоугольником через контракт квадрата) нам нужно привести прямоугольник к этому инварианту.


Кому нужно? Мне не нужно. Мне вобще не нужна эта операция у прямоугольника. Ни у какого.

AVK>>В том, что смысл того функционала, о котором я говорил, применительно к пямоугольнику неясен.


G>А, он и не должен быть ясен применительно к прямоугольнику Его применяют к квадрату.


Что и требовалось доказать.
... << RSDN@Home 1.2.0 alpha rev. 599>>
AVK Blog
Re[8]: Кто автор?
От: NotGonnaGetUs  
Дата: 03.08.05 12:29
Оценка:
Здравствуйте, stalcer, Вы писали:

G>>Причина нарушения ППЛ в нашем случае состоит именно в том, что операции изменения размеров не являются замкнутыми — они выводят квадрат из класса квадратов.


S>Вай-вай, как нехорошо. Вот же мое решение
Автор: stalcer
Дата: 01.08.05
(см. код).



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

Например, создавая Rectangle и выстявляя для него размер 100, 100 мы тем самым создаём тотже самый объект, что и квадрат со стороной 100, так?
Почему же тогда, эти два одинаковых объекта имеют разные методы для работы с ними? (Добавим к квадрату операцию вычисления радиуса вписанной окружности, а прямоугольнику — увеличение одной из сторон в к раза, а другой в 1/к раз).
Re[19]: Кто автор?
От: gbear Россия  
Дата: 03.08.05 12:36
Оценка:
Здравствуйте, AndrewVK, Вы писали:

G>>А, он и не должен быть ясен применительно к прямоугольнику Его применяют к квадрату.


AVK>Что и требовалось доказать.


Доказать-то как раз требывалось наличие у квадрата поведения, не реализуемого прямоугольником. Наличие такого поведение означало бы отсутсвие отношения тип/подтип в терминах behavioural subtyping.

---
С уважением, Сиваков Константин.
Re[20]: Кто автор?
От: Павел Кузнецов  
Дата: 03.08.05 12:39
Оценка: +3
gbear,

g>
 g>     class S
 g>     {
 g>         private int _size;

 g>         public int Size
 g>         {
 g>             get{return _size;}
 g>             set{_size = value;}
 g>         }

 g>         public int Area
 g>         {
 g>             get{return (_size * _size);}
 g>         }
 g>     }
 g>


Т.е. одним из следствий удвоения Size является учетверение Area. Так? (*)

Если не так, то приведи, пожалуйста, спецификацию Size и Area. В частности,
в той части, где описана связь между ними.

g>
 g>     class R: S
 g>     {
 g>         private int _width;

 g>         public int Height
 g>         {
 g>             get{return base.Size;}
 g>             set{base.Size = value;}
 g>         }

 g>         public int Width
 g>         {
 g>             get{return _width;}
 g>             set{_width = value;}
 g>         }

 g>         new public int Area
 g>         {
 g>             get{return (base.Size * _width);}
 g>         }
 g>     }
 g>


Если ответ на (*) положительный, то, очевидно, R изменяет поведение S.

Если ответ на (*) отрицательный, то, вообще, неясно, какой смысл здесь
что-либо наследовать: через S с объектами R ничего осмысленного сделать
не выйдет, т.к. Size и Area, по сути -- "вещи в себе".
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[10]: Кто автор?
От: Павел Кузнецов  
Дата: 03.08.05 12:47
Оценка:
stalcer,

s> Ну, хорошо, пусть о создании новых. А что это меняет в моем рассуждении?

s> Просто вместо операции изменения есть операция создания. И тогда она уже
s> не может быть полиморфна для прямоугольников и квадратов. Именно с точки
s> зрения математики.

Меняется то, что в случае создания для операций, выводящих за диапазон допустимых
значений одного типа, мы вполне можем создавать новые объекты другого типа.
class Rectangle
{
public:
  Rectangle(int w, int h);
  int width() const;
  int height() const;
};

Rectangle set_width( Rectangle const&, int width );
Rectangle set_height( Rectangle const&, int height );

class Square : public Rectangle
{
public:
  Square(int s);
};

В этом случае все более-менее нормально.

Но вот если допустить именно мутирующие операции, то твое рассуждение:

На самом деле, в математиеских изысканиях, мы оперируем понятиями точно также, как объектами в программе.

будет неверным.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Кто автор?
От: Трурль  
Дата: 03.08.05 13:43
Оценка: +1
Здравствуйте, Gaperton, Вы писали:

G>Вот определение наследования:

G>B пронаследован от А тогда и только тогда, когда для любого экземпляра B верно, что
G>1) он является экземпляром А (B подмножество А)
G>2) что модифицирующие операции, определенные в А не выводят экземпляр В из класса В (операции должны быть замкнуты).

Теоретики различают отношение наследования и отношение "тип-подтип" ("is-a"). Наследование означает просто добавление/модификацию методов/свойств. То есть, квадрат вполне может наследовать прямоугольнику (равно как и наоборот), хотя не является подтипом.
Re[22]: Все еще проще
От: Gaperton http://gaperton.livejournal.com
Дата: 03.08.05 14:00
Оценка:
Здравствуйте, gbear, Вы писали:

G>Осталось разубедить Вас в том, что нет необходимости рассматривать всё возможное множество phi.

Интересно, и как же вы это сделаете?
Re[23]: Все еще проще
От: Трурль  
Дата: 03.08.05 14:08
Оценка: 6 (1) :)
Здравствуйте, Gaperton, Вы писали:

G>Здравствуйте, gbear, Вы писали:


G>>Осталось разубедить Вас в том, что нет необходимости рассматривать всё возможное множество phi.

G>Интересно, и как же вы это сделаете?

В качестве первого шага предлагаю ограничится подмножеством вычислимых phi.
Re[8]: Кто автор?
От: Gaperton http://gaperton.livejournal.com
Дата: 03.08.05 14:25
Оценка:
Здравствуйте, stalcer, Вы писали:

G>>Как заканчивались наставления самураев по владению техникой меча, это следует внимательно изучить.


S>Да изучали уже вдоль и поперек . Просто хочется понять вот что: может быть LSP, это не тот идеал, к которому следует стремиться, может быть он именно такой за неимением лучшего решения? Именно из-за недостаточно глубокого понимания основ окружающего нас мира.


Ничего лучше ООП для структурирования программ в императивных языках не придумано, а в ООП от LSP никуда не деться. Хотите стремиться к другому идеалу? Да ради бога, есть две альтернативы. Обе — не так чтобы очень.
1) Динамические языки, например JavaScript. Там вам во многих случаях не понадобится наследование, и вместе с ним уйдет LSP. Хотя до конца вы от наследования не избавитесь.
2) Декомпозиция программы при помощи потоков. Совершенно ортогональный ООП метод, в принципе. Никаких объектов, наследования и LSP, при этом такая же выразительная сила.
Re[16]: Кто автор?
От: Gaperton http://gaperton.livejournal.com
Дата: 03.08.05 16:50
Оценка:
Здравствуйте, gbear, Вы писали:

G>Это — ошибка. Смысл-то как раз в том, что P не должна, как Вы выразились, "понять" что ей "подпихнули" S вместо T.


Это — не ошибка. Я взял двойное отрицание. a == !!a. Читайте внимательнее, это одно и то же.

G> Возвращаясь к квадрату и прямоугольнику: все множество инвариантов квадрата лежит внутри множества инвариантов прямоугольника.


А вот это — ошибка. Инвариант квадрата: Width == Height. У прямоугольника нет такого инварианта.
Вот еще один инвариант квадрата: Area == Width^2 == Height^2. Для прямоугольника не выполняется. Это если говорить о геометрических фигурах.

А теперь о вашем решении (последняя попытка объяснить — ничего нового я вам сказать больше не смогу): Area — инвариант метода S::get_Size (он не меняет площадь, что совершенно естественно и ожидаемо для длины ребра квадрата), но он не является инвариантом его реализации в подклассе R::get_Size. Нарушение этого инварианта будет видимо пользователю S. При этом, исключать это из спецификации S нельзя — с какой стати площадь квадрата должна меняться при попытке узнать длину стороны?

Вот и все. Вы нарушаете принцып Лисков, определение подтипа и вообще все, что можно нарушить. Постусловие метода R::get_Size получается слабее, чем в базовом классе (инвариант метода преобразуется в пару предусловие — постусловие: S::get_Size true -> Area_pred==Area_post==result^2, в то время как R::get_Size true -> Area_post == result^2).
Re[9]: Кто автор?
От: stalcer Россия  
Дата: 04.08.05 05:45
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Например, создавая Rectangle и выстявляя для него размер 100, 100 мы тем самым создаём тотже самый объект, что и квадрат со стороной 100, так?


Не так .
http://www.lmdinnovative.com (LMD Design Pack)
Re[9]: Кто автор?
От: stalcer Россия  
Дата: 04.08.05 05:55
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Добавим к квадрату операцию вычисления радиуса вписанной окружности, а прямоугольнику — увеличение одной из сторон в к раза, а другой в 1/к раз.


Ну и если очень хочется:

class Rectangle
{
    public int Width  
    {
        get { return _width; }
    }
    public int Height
    {
        get { return _height; }
    }
    public virtual void SetBounds(int width, int height)
    {
        _width  = width;
        _height = height;
    }
    public virtual ChangeDims(double k)
    {
        _width  = (int)(_width * k);
        _height = (int)(_height / k);
    }
    
    private int _width;
    private int _height;
}

class Square: Rectangle
{
    public override void SetBounds(int width, int height)
    {
        if (width != height)
            throw Exception("...");
            
        base.SetBounds(width, height);
    }
    public override ChangeDims(double k)
    {
        if (k != 1)
            throw Exception("...");
    }
    public double GetIncircleRadius() { ... }
}


Причем, только, здесь "операция вычисления радиуса вписанной окружности", которая вообще может не быть виртуальной, по твоему условию ?
http://www.lmdinnovative.com (LMD Design Pack)
Re[9]: Кто автор?
От: stalcer Россия  
Дата: 04.08.05 06:08
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Ничего лучше ООП для структурирования программ в императивных языках не придумано.


Дык я и предлагаю подумать. Смотреть в корень, так сказать.

G>1) Динамические языки, например JavaScript. Там вам во многих случаях не понадобится наследование, и вместе с ним уйдет LSP. Хотя до конца вы от наследования не избавитесь.


Проблему прямоугольника и квадрата это никак не решает.

G>2) Декомпозиция программы при помощи потоков. Совершенно ортогональный ООП метод, в принципе. Никаких объектов, наследования и LSP, при этом такая же выразительная сила.


Посмотрю .
http://www.lmdinnovative.com (LMD Design Pack)
Re[10]: Кто автор?
От: NotGonnaGetUs  
Дата: 04.08.05 06:53
Оценка:
Здравствуйте, stalcer, Вы писали:

S>Здравствуйте, NotGonnaGetUs, Вы писали:


NGG>>Например, создавая Rectangle и выстявляя для него размер 100, 100 мы тем самым создаём тотже самый объект, что и квадрат со стороной 100, так?


S>Не так .



Как это не так?

Квадрат получился? Получился.
Имею право вычислить радиус? Имею.

Но не могу, т.к. он прямоугольник

Маленький оффтоп:

Нравится кидать эксепшены, тогда давай и методы квадрата перетащим в прямоугольник, будем там кидать ошибки
А что бы не заморачиваться (с радиусом, с тем что квадрат это не квадрат), лучше совсем не станем вводить "квадрат" как самостоятельный класс
Re[9]: Кто автор?
От: stalcer Россия  
Дата: 04.08.05 06:53
Оценка:
Здравствуйте, Gaperton, Вы писали:

G>Ничего лучше ООП для структурирования программ в императивных языках не придумано.


Мне, например, больше нравится такой подход:

Есть базовый класс A, со своей спецификацией, со своими инвариантами. Есть некая гипотетическая программа P, которая умеет работать с объектами класса A. И есть некий наследник B: A.

Объекты наследника должны формировать подмножество множества объектов предка с точки зрения предметной области. Несмотря на то, что SLP может при этом нарушаться.
Операции в наследнике не дают вывести его из множества объектов наследника.
А вот инварианты могут изменяться.

Тогда эта программа P, при подсовывании ей объекта класса B, должна либо отработать, либо выбросить исключительную ситуацию.

Исключительную ситуацию, естественно, будет в конечном счете выбрасывать переопределенная в классе B операция, которую по ходу дела вызывает программа P.

При таком подходе сохраняется разделение ответственности:

— Класс A не должен знать ни об особенностях класса B, ни о программе P.
— Программа P знает об A, так как изначально предназначена для работы с ним, но не должна знать о B.
— Класс B не обязан знать о программе P.
— Некая подсистема X , использующая P и не использующая объекты B, не знает об особенностях B и не может получить ошибку вызванную этими особенностями.
— Некая подсистема X, использующая P и использующая объекты B, знает об особенностях B и готова получить ошибку вызванную этими особенностями.

Вопрос: как писать программы (P), устойчивые к таким исключительным ситуациям.



И второй момент. Никто еще не отменял эволюционность. В том числе и эволюционное познание предметной области.

Поэтому, если в ходе создания наследников от класса TStream, вдруг обнаруживается, что нектороые потоки бывают ReadOnly, например, TFileStream, то нет ничего плохого, чтобы это только что открытое свойство предметной области ввести в интерфейс базового класса.

То есть независимости предка от его наследников в общем случае нет. Ведь какое-либо множество объектов какого-либо класса предметной области, рассматриваемое нами при проектировании класса в программе, естественным образом включает в себя все возможные подмножества себя (этого множества).
http://www.lmdinnovative.com (LMD Design Pack)
Re[11]: Кто автор?
От: stalcer Россия  
Дата: 04.08.05 07:03
Оценка:
Здравствуйте, NotGonnaGetUs, Вы писали:

NGG>Квадрат получился? Получился.


Если квадрат получился, то, конечно, ты прав. И скорее всего отдельного класса для квадрата вводить не нужно.

Но с моей точки зрения это не квадрат, так как он отличается по поведению. Например, его запросто можно вывести из множества квадратов изменением, скажем, длины. А настоящий квадрат — нельзя.
http://www.lmdinnovative.com (LMD Design Pack)
Re[10]: Кто автор?
От: Gaperton http://gaperton.livejournal.com
Дата: 04.08.05 12:37
Оценка: :)
Здравствуйте, stalcer, Вы писали:

S>Здравствуйте, Gaperton, Вы писали:


G>>Ничего лучше ООП для структурирования программ в императивных языках не придумано.


S>Мне, например, больше нравится такой подход:


S>Есть базовый класс A, со своей спецификацией, со своими инвариантами. Есть некая гипотетическая программа P, которая умеет работать с объектами класса A. И есть некий наследник B: A.


S>Объекты наследника должны формировать подмножество множества объектов предка с точки зрения предметной области. Несмотря на то, что SLP может при этом нарушаться.

S>Операции в наследнике не дают вывести его из множества объектов наследника.
S>А вот инварианты могут изменяться.

S>Вопрос: как писать программы (P), устойчивые к таким исключительным ситуациям.

Никак. В этом его слабая сторона.
Re[10]: Кто автор?
От: Gaperton http://gaperton.livejournal.com
Дата: 04.08.05 12:45
Оценка: +1
Здравствуйте, stalcer, Вы писали:

S>Здравствуйте, Gaperton, Вы писали:


G>>Ничего лучше ООП для структурирования программ в императивных языках не придумано.


S>Дык я и предлагаю подумать. Смотреть в корень, так сказать.


В этой ветке как минимум трижды упоминалось, что именно надо сделать, чтобы при наследовании не нарушился LSP. Вы и я далеко не первые, кто над этим думал, понимаете? Вот ответ — в очередной раз:

Достаточно возвращать измененную копию объекта вместо изменения самого объекта, и все — вы без проблем моделируете вашу математику, не нарушая принцип Лисков.

G>>1) Динамические языки, например JavaScript. Там вам во многих случаях не понадобится наследование, и вместе с ним уйдет LSP. Хотя до конца вы от наследования не избавитесь.


S>Проблему прямоугольника и квадрата это никак не решает.

Решает на счет раз. В корень смотрите , и все будет ок.

В JavaScript вы можете добавлять и удалять методы на лету, т. е. фактически вы можете реализовать изменение типа для объекта. При этом, вам вообще не нужен базовый класс, так как вызовы диспетчеризуются динамически. А нет базового класса, нет и LSP. Как ни странно, объекты при этом есть, и полиморфизм остается.
Re[12]: Кто автор?
От: NotGonnaGetUs  
Дата: 04.08.05 13:02
Оценка:
Здравствуйте, stalcer, Вы писали:

S>Здравствуйте, NotGonnaGetUs, Вы писали:


NGG>>Квадрат получился? Получился.


S>Если квадрат получился, то, конечно, ты прав. И скорее всего отдельного класса для квадрата вводить не нужно.


S>Но с моей точки зрения это не квадрат, так как он отличается по поведению. Например, его запросто можно вывести из множества квадратов изменением, скажем, длины. А настоящий квадрат — нельзя.


Я о том же. В одной задаче выделять квадрат нужно, и это можно сделать выделив общего наследника для этих объектов (с общими методами и без методов модификации размеров), в другой — не нужно абсолютно.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.