Re[19]: Почему объектно-ориентированное программирование про
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.02.11 17:03
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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


S>>>>не помню, кто это сказал


I>>>В большинстве случаев type test не нужен и использование его создаёт проблемы. PM — это как раз тот случай где type test это нормально.

S>>Для АТД и ПМ type test — это нормально. Для иерархии объектов, моделирующих АТД — это не алё.

I>Кто такое сказал ? Какие проблемы возникают ? Какие есть более толковые решения ? Или же вопрос чисто идеологический ?


Почитай у Кириевски, ты любишь на него ссылаться.
Re[36]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 24.02.11 17:38
Оценка:
Здравствуйте, Ikemefula, Вы писали:


I>Я тебе дал одну конкретную задачу. Которая вобщем то достаточно часто встрачается


Вообще-то, твое решение часто встречается, а не задача. Ты продолжаешь путать уровни задач. Взяв за основу некое решение, ты выходишь на список новых задач, требуемых для его реализации. И приводишь тут этот список, вместо исходной задачи. Людям порой трудно объяснить, чтобы они не плясали от первого же попавшегося решения. Я уже предложил воспользоваться личкой, если неохота тут светить реальные куски кода.
Re[32]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 24.02.11 20:35
Оценка:
Здравствуйте, Ikemefula, Вы писали:

V>>Чем обеспечивается зависимость? Впрочем, это тот же вопрос, что и предыдущий. Мои наихудшие подозрения оправдались.


I>Я уже внятно ответил на этот вопрос в твоем же коде — http://rsdn.ru/forum/philosophy/4171506.1.aspx
Автор: Ikemefula
Дата: 24.02.11


Ты про это:

Состояния может и не быть, а зависимость будет в любом случае.


Т.е. зависимость не постоянная, а наблюдается лишь в некоторых методах в виде поданных аргументов? Хе, такая зависимость режется проще всего, как раз через упомянутый тобой DI, или функциональный подход (одно и то же).

I>Что бы продолжить разговор про депенденси, ответь сначала вот на этот вопрос http://rsdn.ru/forum/philosophy/4171487.1.aspx
Автор: Ikemefula
Дата: 24.02.11

I>Походу, код показать ты не в состоянии

Походу тебе нечем заняться как форуме болтать. Иначе откуда такая незаинтересованность в ответах на собственные вопросы? Там по ссылке всё есть.

I>Прочесть коммент болдом ты не в состоянии ? Надо было сразу сказать


Ну ты нахал, однако:

>Вот есть твои методы PreDo() и PostDo(). Они требовали для своей работы каких-то внутренних полей первоначального класса Doer?
Разумеется не требовали и это элементарно.
>Если требовали, то тебе надо обеспечить доступ к этим полям в новом объекте PreAndPostDoerDecorator, ЧТД.
Во первых, не требовали. Во вторых, они могут пользоваться методами базового класса



I>Ты сам о нем говоришь. А я показал все что необходимо.


Из того, что ты показал не следует твое решение. Вообще.


I>есть фреймворк для рендеринга изображения. в нем реализованы различные генераторы изображения. каждая такая рисовалка должена реализовать интерфейс IRender(=IDoer) с единственным иметодом void Render(Context ctx); кроме этого есть еще рисовалки реализованые в проекте.



I>необходимо расширить возможности этого фреймворка диагностикой, валидацией, отладкой и тд


I>задача 1 — замерить время выполнения некоторых реализаций IRender — любых, как своих так и встроеных. Почему нельзя замерить через профайлер — нужны данные по продакшну, а не по дебагу

I>задача 2 — параллельно с генерацией изображения записывать генерацию в текстовом виде для валидации, диагностики, отладки
I>задача 3 — некоторые генераторы косячат. надо подавить их артефакты без перепеисывания. например перед вызовом генератора установить некоторые значения в контексте, а по окончанию вернуть значения назад.
I>задача 4 — необходимо иметь возможность включать-выключать некоторые генераторы, например при рендеринге на бумагу или экран
I>задача 5 — необходимо иметь возможность делать отрисовку по требованию, т.е. вместо изображения генерируются команды отрисовки примитивов, который потом можно выполнить в другом фреймворке.

I>в моем решении ненужно модифицировать ни генераторы, не фреймворк, вообще ничего нужно только сделать один декоратор на каждую задачу.


I>при этом для задач 1,2,3 и 4 декоторатору вообще не надо ничего знать ни про базовый, ни про Сontext.


I>1 использует StopWatch и Logger

I>2 использует логгер
I>3 использует контекст
I>4 ничего вообще не использует — у тебя вроятно фантазия не позволит представить такое

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


I>и только 5й использует кое какие методы из базового класса и класса Context


I>покажи внятное, тестируемое решение. если не можешь, объясни внятно, чего тебе не хватает.



Обычная декомпозиция на основе SR. Например так:
interface IRenderer
{
  void Render(Context ctx);
}

interface IRenderAspect 
{
  void Pre(IRenderer renderer, Context ctx);
  void Post(IRenderer renderer, Context ctx);
}

class RendererWithAspect : IRenderer
{
  IRenderAspect _aspect;
  IRenderer _renderer;

  public RendererWithAspect(IRenderAspect aspect, IRenderer renderer) 
  { 
    _aspect = aspect; 
    _renderer = renderer;
  }

  public void Render(Context ctx)
  {
    _aspect.Pre(_renderer, ctx);
    _renderer.Render(ctx);
    _aspect.Post(_renderer, ctx);
  }
}


Итого, создавай независимые аспекты, реализующие IRendererAspect, а затем порождай "матрешки" применённых аспектов произвольной вложенности, то бишь комбинируй как угодно аспекты через композицию RendererWithAspect.

Состояние конкретного экземпляра RendererWithAspect определяется конкретными значениями его 2-х приватных полей. Если придти к этому решению от некоего Renderer, в который внутри, помимо собственно рисования, находился отладочный код, то декомпозиция состояния здесь видна невооруженным взглядом. Сейчас Renderer должен только рисовать, а отладочная фигня лежит внутри IRenderAspect. Если некий составной рендерер использует другие рендереры, т.е. сам представляет из себя композицию, то его отладочный вариант аналогично строится на оборачивании каждого вложенного рендерера через RendererWithAspect.

Помимо показанного, комбинировать аспекты можно и явно:
class AspectStack : IRenderAspect
{
  IRenderAspect _top;
  IRenderAspect _next;

  public AspectStack(IRenderAspect top, IRenderAspect next)
  {
    _top = top;
    _next = next;
  }

  public void Pre(IRenderer renderer, Context ctx)
  {
    _top.Pre(renderer, ctx);
    _next.Pre(renderer, ctx);
  }

  public void Post(IRenderer renderer, Context ctx)
  {
    _next.Post(renderer, ctx);
    _top.Post(renderer, ctx);
  }
}



Изоляция тестирования тут простая — отдельно тестируются аспекты на заглушечном renderer, отдельно тестируется каждый рендерер, через заглушечный Context, отдельно сам Context и отдельно RendererWithAspect с обоими заглушечными (хотя не факт, что его примитивный код требует тестирования).

Пример тут: http://rsdn.ru/forum/philosophy/4172198.aspx
Автор: vdimas
Дата: 24.02.11
Re[20]: Почему объектно-ориентированное программирование про
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.02.11 20:54
Оценка:
Здравствуйте, samius, Вы писали:

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


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


S>>>>>не помню, кто это сказал


I>>>>В большинстве случаев type test не нужен и использование его создаёт проблемы. PM — это как раз тот случай где type test это нормально.

S>>>Для АТД и ПМ type test — это нормально. Для иерархии объектов, моделирующих АТД — это не алё.

I>>Кто такое сказал ? Какие проблемы возникают ? Какие есть более толковые решения ? Или же вопрос чисто идеологический ?


S>Почитай у Кириевски, ты любишь на него ссылаться.


А причем тут криевски?
PM по АлгТД это далеко не тоже самое что typetest в ООП.

Для объектов x is A будет верно для любого наследника A, а когда еще и множественное наследование (реализация множества интерфейсов) есть, то этот код будет работать медленно. Кстати именно поэтому классы атрибутов в .NET рекомендуют делать sealed, тогда поиск атрибутов определенного типа работает быстрее.

АлгТД не образуют иерархию, а предоставляют известное на момент компиляции количество вариантов. Поэтому для определения конкретного варианта можно использовать числовой идентификатор и код матчинга будет ужасно эффективным (выбор по таблице).
Re[21]: Почему объектно-ориентированное программирование про
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.02.11 22:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


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


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


S>>>>>>не помню, кто это сказал


I>>>>>В большинстве случаев type test не нужен и использование его создаёт проблемы. PM — это как раз тот случай где type test это нормально.

S>>>>Для АТД и ПМ type test — это нормально. Для иерархии объектов, моделирующих АТД — это не алё.

I>>>Кто такое сказал ? Какие проблемы возникают ? Какие есть более толковые решения ? Или же вопрос чисто идеологический ?


S>>Почитай у Кириевски, ты любишь на него ссылаться.


G>А причем тут криевски?

G>PM по АлгТД это далеко не тоже самое что typetest в ООП.
конечно не то же самое. Речь, видимо, лишь об идентификации конструктора АТД.

G>Для объектов x is A будет верно для любого наследника A, а когда еще и множественное наследование (реализация множества интерфейсов) есть, то этот код будет работать медленно. Кстати именно поэтому классы атрибутов в .NET рекомендуют делать sealed, тогда поиск атрибутов определенного типа работает быстрее.

Согласен. Но при моделировании АТД иерархией наследования, достаточно проверки на точное соответствие
o.GetType() == typeof(SomeADTConstructor)
т.к. иерархия будет двухэтажная (не считая System.Object, если речь о дотнете).

G>АлгТД не образуют иерархию, а предоставляют известное на момент компиляции количество вариантов. Поэтому для определения конкретного варианта можно использовать числовой идентификатор и код матчинга будет ужасно эффективным (выбор по таблице).

образует иерархию (иерархией принято называть дерево наследования вне зависимости от того, известно ли оно все во время компиляции). Да, будет эффективно выполняться. С другой стороны — числовой идентификатор ведет к куче рукописного кода (по идентификатору + метод на каждый вариант). И если код вариантов не генерируется, то это довольно печально.

Но мы говорим вообще об ООП, а не конкретно о ООП via C#. Например, в C++ рассчитывать на наличие RTTI не всегда можно, особенно разрабатывая библиотеки, которые будут использоваться фиг знает где. Да и бытует мнение среди C++-ников, что если потребовался RTTI, значит что-то делается не по ООП. Так что идентификаторы вариантов — практически единственный кошерный путь в С++. Это если речь только об идентификации варианта. Если же нужно сравнение двух АТД (одного типа АТД, но возможно разных вариантов), то оно выливается уже в Template Method, т.к. надо сравнить и идентификаторы вариантов, и вызывать специфичный для конкретных вариантов код проверки равенства.
Но зато в C++ все это можно повесить на препроцессор и шаблоны.
Re: Почему объектно-ориентированное программирование провали
От: Donz Россия http://donz-ru.livejournal.com
Дата: 25.02.11 07:18
Оценка: :)
Здравствуйте, Игорь САВЧУК, Вы писали:

По-моему, это все же сарказм. Одно упоминание эфиродинамики и ненужности ТО чего стоит.
Или у автора совсем все плохо.
Re[21]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 09:24
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Мне демонстрировать ручную заглушку для этого случая или и так всё понятно? Хотя, и для первого случая заглушка может быть полезна для того, например, чтобы проверить, что этот вызов-таки был произведен в нужном месте. Чтобы не вручную, можно юзать какой-нить mock framework:


Не валяй дурака. Тебе надо было протестировать твой код, а не ThirdParty. Посему я скипнул порожний и неотносящийся к делу код. Похоже ты вообще не понимаешь, что пишешь

Показываю проблему тестирования в ТВОЕМ коде:

// тут наследуем, т.к. PreDo()/PostDo() требуют особого доступа к базе по условию,
class PreAndPostDecorator : ThirdPartyClass

ThirdPartyClass - ЭТО ЖОСТКАЯ ЗАВИСИМОСТЬ КОТОРАЯ ПРЕПЯТСТВУЕТ ТЕСТИРОВАНИЮ


{
  public void PreDo() {}
  public void PostDo() {}
}

// структурная композиция
class PreAndPostDoerDecorator : IDoer
{
  private PreAndPostDecorator _decorator = new PreAndPostDecorator();

ВОТ ЭТО ЖОСТКАЯ ЗАВИСИМОСТЬ КОТОРАЯ ПРЕПЯТСТВУЕТ ТЕСТИРОВАНИЮ

 
  PreAndPostDoerDecorator(IDoer doer){  _doer = doer;} 

  void Do() {
    _decorator.PreDo();
    _doer.Do();
    _decorator.PostDo();
  }
}

interface IDoer
{
  void Do(); 
}



Твой код НЕ ТЕСТИРУЕМЫЙ В ПРИНЦИПЕ, ТК НЕЛЬЗЯ ОТДЕЛИТЬ PreAndPostDoerDecorator от ThirdPartyClass, т.е

итого, что надо сделать, что бы твой декоторатор был тестируемым ?

// тут наследуем, т.к. PreDo()/PostDo() требуют особого доступа к базе по условию,
class PreAndPostDecorator : ThirdPartyClass
{
  public virtual void PreDo() {}  // СМОТРИМ ВНИМАТЕЛЬНО - virtual
  public virtual void PostDo() {} // СМОТРИМ ВНИМАТЕЛЬНО - virtual
}

class PreAndPostDoerDecorator : IDoer
{
  private PreAndPostDecorator _decorator;

  public PreAndPostDoerDecorator(PreAndPostDecorator d)
  {
    decorator = d;
  }

  public PreAndPostDoerDecorator(PreAndPostDecorator d)
  {
    decorator = = new PreAndPostDecorator();
  }
 
  PreAndPostDoerDecorator(IDoer doer){  _doer = doer;} 

  public void Do() {
    _decorator.PreDo();
    _doer.Do();
    _decorator.PostDo();
  }
}

interface IDoer
{
  void Do(); 
}


и сам тест

[TestMethod]
public void TestDoerDecorator
{
var thirdPartyMock = new Mock<PreAndPostDecorator>();

thirdPartyMock.Setup(x => x.PreDo());
thirdPartyMock.Setup(x => x.PostDo());

var decorator = new PreAndPostDoerDecorator(thirdPartyMock.Object);

decorator.Do();

Assert.IsTrue(thirdPartyMock.Verify(x => x.PreDo()),Times.Once());
Assert.IsTrue(thirdPartyMock.Verify(x => x.PostDo()),Times.Once());
}

Очевидно, решение не самое лучшее, т.к. PreAndPostDecorator вообще не нужен. Все что надо сделать — добавить возможность инжектить ThirdParty прямо в декоторатор ровно тем же способом, который был показан чуть выше.
Re[33]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 09:52
Оценка:
Здравствуйте, vdimas, Вы писали:

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


V>>>Чем обеспечивается зависимость? Впрочем, это тот же вопрос, что и предыдущий. Мои наихудшие подозрения оправдались.


I>>Я уже внятно ответил на этот вопрос в твоем же коде — http://rsdn.ru/forum/philosophy/4171506.1.aspx
Автор: Ikemefula
Дата: 24.02.11


V>Ты про это:


Я про код где коммент выделен жирным.

I>>Что бы продолжить разговор про депенденси, ответь сначала вот на этот вопрос http://rsdn.ru/forum/philosophy/4171487.1.aspx
Автор: Ikemefula
Дата: 24.02.11

I>>Походу, код показать ты не в состоянии

V>Походу тебе нечем заняться как форуме болтать. Иначе откуда такая незаинтересованность в ответах на собственные вопросы? Там по ссылке всё есть.


По ссылке ничего нет — ты не в состоянии написать юнит-тест для своего же класса

V>Ну ты нахал, однако:

V>

>>Вот есть твои методы PreDo() и PostDo(). Они требовали для своей работы каких-то внутренних полей первоначального класса Doer?
V>Разумеется не требовали и это элементарно.


Смотри внимательно — последняя фраза моя. В ней говорится что никаких внутренних полей для работы не требуется.

I>>1 использует StopWatch и Logger

I>>2 использует логгер
I>>3 использует контекст
I>>4 ничего вообще не использует — у тебя вроятно фантазия не позволит представить такое

V>Естественно не позволит, для включения генераторов надо иметь на них ссылку.


Твое не позволит. А мое позволяет — см мой пример.

V>Обычная декомпозиция на основе SR. Например так:

V>
V>


Задача 4 в принципе не решаема твоим кодом. т.е. для решения которое не покрывает все случаи ты нагородил всяких классов, аспектов и интерфейсов

V>Итого, создавай независимые аспекты, реализующие IRendererAspect, а затем порождай "матрешки" применённых аспектов произвольной вложенности, то бишь комбинируй как угодно аспекты через композицию RendererWithAspect.


см выше

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

someThirPartyObject.AcceptRender(widget1.Suppress()); 
someThirPartyObject.AcceptRender(widget2.Measure()); 
someThirPartyObject.AcceptRender(widget3.Log());
someThirPartyObject.AcceptRender(widget4.FixAliasing());
someThirPartyObject.AcceptRender(widget5.Deferred());

...
// вот так "аспекты" комбинируются - и без всяких одноразовых говноинтерфейсов
someThirPartyObject.AcceptRender(widget10234.Deferred().FixAliasing().Measure().Log());
...

// а теперь тем же способом рендерим всю сцену
someThirPartyObject.Log().Measure().Render(new Context());


Вот как это сделано, без экстеншнов, они тривиальные

interface IRender
{
   void Render(Context ctx);
}

// IRender реализует как мой код, так и ThirdParty, потому я не могу изменить поведение Render

// базовый для декотораторов
abstract class RenderDecoratorBase : IRender
{
  protected IRender _render;
  RenderDecoratorBase(IRender render)
  { 
      _render = render;
  }
 
  public abstract void Render(Context ctx);
}

задача 4 - маскировать рендеринг некоторых объектов

class SuppressRendrer : RenderDecoratorBase
{
  SuppressRendrer(IRender render):base(render)
  { 
  }
 
  public void Render(Context ctx) { // empty method } // :-) - этот случай тебе фантазия и не подсказала :-)
}

// вот как используется этот декоратор - точно так же, как и все другие.

someThirPartyObject.AcceptRender( new SuppressRendrer(widgetThatIsToBeHidden) );


задача 2 - логировать рендеринг некоторых объектов

class LoggingRendrer : RenderDecoratorBase
{
  LoggingRendrer(IRender render):base(render)
  { 
  }
 
  public void Render(Context ctx) 
  { 
     Log.Write("RenderBegin"); 
     
     _render.Render(ctx);
     
     Log.Write("RenderEnd");
  } // :-) 
}

someThirPartyObject.AcceptRender( new LoggingRendrer(widgetThatIsToBeLogged) );



V>Изоляция тестирования тут простая — отдельно тестируются аспекты на заглушечном renderer, отдельно тестируется каждый рендерер, через заглушечный Context, отдельно сам Context и отдельно RendererWithAspect с обоими заглушечными (хотя не факт, что его примитивный код требует тестирования).


V>Пример тут: http://rsdn.ru/forum/philosophy/4172198.aspx
Автор: vdimas
Дата: 24.02.11


это не пример,а уловка для ухода от ответа. Смотри внимаетельно :

http://rsdn.ru/forum/philosophy/4172932.1.aspx
Автор: Ikemefula
Дата: 25.02.11
Re[37]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 09:56
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Вообще-то, твое решение часто встречается, а не задача.


И задача и решение. Когда работаешь с thirdparty это обычное дело.

Порожнее скипнул.
Re[20]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 09:59
Оценка:
Здравствуйте, samius, Вы писали:

I>>>>В большинстве случаев type test не нужен и использование его создаёт проблемы. PM — это как раз тот случай где type test это нормально.

S>>>Для АТД и ПМ type test — это нормально. Для иерархии объектов, моделирующих АТД — это не алё.

I>>Кто такое сказал ? Какие проблемы возникают ? Какие есть более толковые решения ? Или же вопрос чисто идеологический ?


S>Почитай у Кириевски, ты любишь на него ссылаться.


Ты про визитор ?

Современные ООП языки не умеют PM, сделано это не ради идеологии, а из за сложности реализации синтаксиса.

Даже в функциональных язвках не хватет скобок и прочей дряни и приходится прибегать к [|||||]

Если не про визитор, то см. выше

Вообще понимать ООП как парадигму в корне неверно. Парадигмы есть разные, функциональная, императивная. ООП ложится и на ту, и на другую.

Так шта...
Re[20]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 10:03
Оценка:
Здравствуйте, samius, Вы писали:

I>>А где ты в них мутабельности углядел ?

S>Хочется спросить навстречу, где ты в них мутабельности не углядел?
S>итератор сохраняет позицию, в наблюдателе subject сохраняет список наблюдателей.

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

S>>>паттерн не определяется назначением и обязанностями. Структура и участники тоже входят в паттерн.


I>>структура, участники, обязанности, зависимости остаются на своих местах

S>Тогда попрошу продемонстрировать, как объект меняет свое поведение без изменения состояния

_currentState.Method();

меняется на

СurrentState().Method();

Все. Больше никаких изменений не нужно, ну и currentState выкинуть.
Re[12]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 10:25
Оценка:
Здравствуйте, FR, Вы писали:

FR>А таких "чисто сферических" ФЯ не существует. Во всех реальных ФЯ есть еще один кубик — модуль, который отлично прячет все кишки. Например в тех же ML образных модули даже позволяют разную инкапсуляцию для разных клиентов использующих данный модуль.


Кишки вообще везде прячутся,только везде прячутся разные кишки. Собственно ориентированый и означает, какие именно кишки прячутся.
Re[21]: Почему объектно-ориентированное программирование про
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.02.11 11:02
Оценка: +1
Здравствуйте, Ikemefula, Вы писали:

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


I>>>Кто такое сказал ? Какие проблемы возникают ? Какие есть более толковые решения ? Или же вопрос чисто идеологический ?


S>>Почитай у Кириевски, ты любишь на него ссылаться.


I>Ты про визитор ?


I>Современные ООП языки не умеют PM, сделано это не ради идеологии, а из за сложности реализации синтаксиса.

OCaml — современный объектно-ориентированный язык


Если ты про C++, VB и C#, то куда им ПМ, если в них нет даже тюплов и АТД
Про сложность реализации синтаксиса — это ты тоже здорово махнул

I>Так шта...

Re[21]: Почему объектно-ориентированное программирование про
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.02.11 11:10
Оценка:
Здравствуйте, Ikemefula, Вы писали:

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


S>>Тогда попрошу продемонстрировать, как объект меняет свое поведение без изменения состояния


I>_currentState.Method();


I>меняется на


I>СurrentState().Method();


I>Все. Больше никаких изменений не нужно, ну и currentState выкинуть.


Оригинальная трактовка неизменяемости
Re[22]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 11:22
Оценка:
Здравствуйте, samius, Вы писали:

I>>Ты про визитор ?


I>>Современные ООП языки не умеют PM, сделано это не ради идеологии, а из за сложности реализации синтаксиса.

S>

S>OCaml — современный объектно-ориентированный язык

S>

Это в первую очередь функциональный, а уже потом — оо. Но раз тебе охота поцепляться к словам, валяй

S>Если ты про C++, VB и C#, то куда им ПМ, если в них нет даже тюплов и АТД

S>Про сложность реализации синтаксиса — это ты тоже здорово махнул

Думаешь это я причиной тому, что C++, java, C# это языки с самым сложным синтаксисом ?
Re[22]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 25.02.11 11:42
Оценка: -1
Здравствуйте, Ikemefula, Вы писали:

I>Показываю проблему тестирования в ТВОЕМ коде:


I>
I>// тут наследуем, т.к. PreDo()/PostDo() требуют особого доступа к базе по условию,
I>class PreAndPostDecorator : ThirdPartyClass
I>ThirdPartyClass - ЭТО ЖОСТКАЯ ЗАВИСИМОСТЬ КОТОРАЯ ПРЕПЯТСТВУЕТ ТЕСТИРОВАНИЮ


ИМХО, не надо быть мега-спецом, чтобы понять описанные способы:

1. Использование собственных интерфейсов/абстрактных классов, суть фасадов/адаптеров третьесторонних классов, вместо использования их напрямую. Одна реализация интерфейсов будет "реальная", остальные — заглушечные в целях тестирования.
2. Использование дотнетной техники CBO. Не обязательно ручками, можно брать готовые mock-фреймворки.
3. Создание целых заглушечных сборок, содержащих одноименные используемые классы в тех же неймспейсах, что замещаемые сборки. С заглушечной же реализацией лишь минимального мн-ва используемых членов. Линковка тестовой сборки с ней. Я иногда прямо тестовые исходники в проект тестовых сборок включаю, особенно если самих тестовых сборок много и много тестовых уникальных "заглушечных" сценариев. Тоже потянет.


Какой из 3-х способов вызывает трудности?

Ты отчасти правильно пишешь:

Тебе надо было протестировать твой код, а не ThirdParty.

Но лишь отчасти. Третьесторонний код тоже иногда приходится тестировать, когда возникают сомнения в его надежности (хотя в таких случаях полезней от сомнительного третьестороннего кода отказаться). Его можно тестировать по "артефактам", коль он порождает сайд-эффекты при своей работе. Вот их и тестируют. Даже если это запись в файл/базу или вывод сообщения протокола в сокет. Значит в тесте через независимое соединение лезешь в базу и проверяешь данные, читаешь файл, слушаешь сокет. Если без сайд-эффекта, то вопрос тестирования еще проще и сравним с тестированием одной ф-ии.

Код наследника от "черного ящика" точно так же тестируется. Этот код надо лишь отделить от базы.

Показываю техники для всех 3-х вариантов.
1.
class ThirdParty {
  protected abstact int TemplateMethod();
  protected void EntryPoint() {}
}

interface IThirdPartyCallback {
  int TemplateMethod();
}

interface IThirdPartyAPI {
  void EntryPoint();
}

class ThirdPartyAdapter : ThirdParty, IThirdPartyAPI {
  IThirdPartyCallback _callback;

  public ThirdPartyAccess(IThirdPartyCallback callback) { _callback = callback; }
  new public void EntryPoint() { base.EntryPoint(); }
  protected override int TemplateMethod() { return _callback.TemplateMethod(); }
}

public delegate IThirdPartyAPI ThirdPartyFactory(IThirdPartyCallback callback);

class InternalParty : IThirdPartyCallback {
  IThirdPartyAPI _api;

  public InternalParty(ThirdPartyFactory factory) { _api = factory(this); }
  public void UsefulMethod() { _api.EntryPoint(); } 
  private int IThirdPartyCallback.TemplateMethod() { return 42; }
}


Итого, InternalParty тестируется отдельно от ThirdParty, через заглушку, реализующую IThirdPartyAPI. В случае, когда наследование не требуется, вся развязка с целью тестопригодности делается одним интерфейсом или абстрактным классом.

2. См DbTest()в последнем сниппете: http://rsdn.ru/forum/philosophy/4172198.1.aspx
Автор: vdimas
Дата: 24.02.11

Подкорректируй под систему типов из способа 1. Или оставь как есть, если наследование от ThirdParty не нужно.

3. Самое что ни на есть решение "в лоб". Создаешь сборку, где в нужных неймспейсах пишешь тестового двойника ThirdParty под некоторые сценарий, с минимально необходимым интерфейсом. Линкуешь тестируемый код с этой сборкой, либо просто создаешь тестовую сборку из нужного набора файлов-исходников, куда включаешь исходный код двойника в т.ч.

Задача двойника — проверять в тестовом сценарии правильность протокола вызова его же методов внешним классом, и выдавать для этих сценариев ожидаемые ответные реакции. Т.е., рисовать в некое устройство не надо, надо лишь проверить, что тестируемый класс правильно вызывает наши методы рисования. Применяя этот способ, можно не заботиться насчет всей этой развязки на интерфейсах.

Способ, с одной стороны, самый трудоемкий, с другой — самый гибкий и самый приближенный к происходящему. Например, если у нас высоконагруженное решение, то все эти лишние развязки через интерфейсы могут заметно повлиять на производительность, поэтому "ручные" заглушки для пары мест в проекте могут оказаться кстати. Из наблюдений, сей подход практически изжил себя из-за обилия mock-фрейморков. Или народ хитрит, и делает объекты через MarshalByRefObject, чтобы юзать технику стабов из ремоутинга.


I>ВОТ ЭТО ЖОСТКАЯ ЗАВИСИМОСТЬ КОТОРАЯ ПРЕПЯТСТВУЕТ ТЕСТИРОВАНИЮ


Конечно, препятствует. Фигли:

Я скипнул порожний текст.


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


I>итого, что надо сделать, что бы твой декоторатор был тестируемым ?


Пройтись по интернету насчет более подробной инфы по указанным способам. Потом поставить у себя технический эксперимент. Возможно — задать пару вопросов в тематических форумах. В итоге, подобрать для вашей инфраструктуры наиболее удобный способ из описанных, или их комбинацию.
Re[22]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 25.02.11 11:53
Оценка: -1
Здравствуйте, Ikemefula, Вы писали:

I>Очевидно, решение не самое лучшее, т.к. PreAndPostDecorator вообще не нужен.


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

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


Ты уже заблудился. В приведенном тобой примере ты тестируешь PreAndPostDoerDecorator, т.е. вот этот код:
    _decorator.PreDo();
    _doer.Do();
    _decorator.PostDo();

А уже высказывал свои сомнения относительно необходимости писать для него тест.

Ты напиши изолированный тест для PreAndPostDecorator, который, например, нетривиально пользует базовый ThirdPartyClass. Вот что представляет интерес для тестирования.
Re[23]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 12:23
Оценка:
Здравствуйте, vdimas, Вы писали:

V>ИМХО, не надо быть мега-спецом, чтобы понять описанные способы:

V>Какой из 3-х способов вызывает трудности?

Никакой. Тебе просто хочется поиграть в какую то свою задачу.

Что бы распознать вызов PreDo и PostDo не нужен ни лишний класс, как у тебя, ни ушлые техники тестирования вроде подмены сборки.

V>Код наследника от "черного ящика" точно так же тестируется. Этот код надо лишь отделить от базы.


Что ж ты его не отделил ?

V>Показываю техники для всех 3-х вариантов.

V>1.
V>
V>class ThirdParty {
V>  protected abstact int TemplateMethod();
V>  protected void EntryPoint() {}
V>}

Ну и клоунада, выбрал удобный для себя интерфейс этого ThirdParty  :))) Ну да бог с ним.

V>interface IThirdPartyCallback {
V>  int TemplateMethod();
V>}

Ага, вводить лишний интерфейс чисто для тестирования :-)

V>interface IThirdPartyAPI {
V>  void EntryPoint();
V>}

и еще один  :))) 

V>class ThirdPartyAdapter : ThirdParty, IThirdPartyAPI {
V>  IThirdPartyCallback _callback;

V>  public ThirdPartyAccess(IThirdPartyCallback callback) { _callback = callback; }
V>  new public void EntryPoint() { base.EntryPoint(); }
V>  protected override int TemplateMethod() { return _callback.TemplateMethod(); }
V>}

И родился небольшой монстрик, цель которого - обеспечить тестируемость ПРИМИТИВНОГО кода 

V>public delegate IThirdPartyAPI ThirdPartyFactory(IThirdPartyCallback callback);

V>class InternalParty : IThirdPartyCallback {
V>  IThirdPartyAPI _api;

V>  public InternalParty(ThirdPartyFactory factory) { _api = factory(this); }
V>  public void UsefulMethod() { _api.EntryPoint(); } 
V>  private int IThirdPartyCallback.TemplateMethod() { return 42; }
V>} 
V>


V>Итого, InternalParty тестируется отдельно от ThirdParty, через заглушку, реализующую IThirdPartyAPI. В случае, когда наследование не требуется, вся развязка с целью тестопригодности делается одним интерфейсом или абстрактным классом.


А теперь вернемся к том, что было — ThirdParty это ThirdParty и менять его нельзя за неимением кода.

V>3. Самое что ни на есть решение "в лоб". Создаешь сборку, где в нужных неймспейсах пишешь тестового двойника ThirdParty под некоторые сценарий,


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

V>Способ, с одной стороны, самый трудоемкий, с другой — самый гибкий и самый приближенный к происходящему.


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

I>>итого, что надо сделать, что бы твой декоторатор был тестируемым ?


V>Пройтись по интернету насчет более подробной инфы по указанным способам. Потом поставить у себя технический эксперимент. Возможно — задать пару вопросов в тематических форумах. В итоге, подобрать для вашей инфраструктуры наиболее удобный способ из описанных, или их комбинацию.


Повеселил так повеселил Ты заметил, что у тебя заюзаны чуть не все возможности дотнета что бы задетектить вызов двух методов ?

Тоже неплохо — новое слово в поведенческом тестировании С твоего позволения я запощу ссылку в Тестирование, шоб люди просветились.
Re[23]: Почему объектно-ориентированное программирование про
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 25.02.11 12:25
Оценка:
Здравствуйте, vdimas, Вы писали:

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


V>Ты уже заблудился. В приведенном тобой примере ты тестируешь PreAndPostDoerDecorator, т.е. вот этот код:


Конечно. Его и надо тестировать, а не ThirdParty. Это называется поведенческое тестирование Цель теста — гарантировать что декорация работает как положено, не вносит неустановленого поведения.


V>Ты напиши изолированный тест для PreAndPostDecorator, который, например, нетривиально пользует базовый ThirdPartyClass. Вот что представляет интерес для тестирования.


Точно такой же подход.
Re[24]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 25.02.11 14:50
Оценка:
Здравствуйте, Ikemefula, Вы писали:

V>>ИМХО, не надо быть мега-спецом, чтобы понять описанные способы:

V>>Какой из 3-х способов вызывает трудности?

I>Никакой. Тебе просто хочется поиграть в какую то свою задачу.


Способ изолирования тестирования через заглушки живет вне "каких-то своих" задач.


I>Что бы распознать вызов PreDo и PostDo не нужен ни лишний класс, как у тебя, ни ушлые техники тестирования вроде подмены сборки.


Дык, ты упомянул всего один класс, и тот лишь адаптер, не содержащий целевого кода. Где еще 2 независимых теста для базы и наследника?


V>>Код наследника от "черного ящика" точно так же тестируется. Этот код надо лишь отделить от базы.


I>Что ж ты его не отделил ?


Проблемы с прочтением кода?


I>Ну и клоунада, выбрал удобный для себя интерфейс этого ThirdParty Ну да бог с ним.


Это не "удобный интерфейс", а те самые два аспекта, которые диктуют нам наследование. В ином случае мы бы наследование не рассматривали.


I>Ага, вводить лишний интерфейс чисто для тестирования


В этом и состоит один из способов. Характерно то, что будучи введен лишь для обеспечения тестопригодности, "лишний интерфейс" порой начинают использовать и для целевых прикладных вещей. Ведь происходящее — суть обеспечение изолированности, а оно бывает удобным не только для теста.


V>>interface IThirdPartyAPI {

V>> void EntryPoint();
V>>}

I>и еще один


Угу. А всего два. Независимо от сложности базового класса.

I>И родился небольшой монстрик, цель которого — обеспечить тестируемость ПРИМИТИВНОГО кода


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


I>А теперь вернемся к том, что было — ThirdParty это ThirdParty и менять его нельзя за неимением кода.


А где ты видел, что я его менял? Чукча писатель?


I>За такую "тестируемость" надо выпиливать из проекта вместе с кодом.


За непроходимость и предрассудки надо выпиливать.


I>Это решение для тех случаев, когда по другому ну никак не выкрутиться, когда другие тесты просто не работают.


Спасибо, что процитировал меня. Только пользуйся тегом [q] в другой раз.


V>>Способ, с одной стороны, самый трудоемкий, с другой — самый гибкий и самый приближенный к происходящему.


I>Способ самый ушлый из всех доступных. нет ни наглядности, не простоты реализации, а эффект ровно тот же, как и в других случаях.


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

Внутренности такого класса можно сделать на моках, в том же стиле, как ты написал тестовый код. Более того, это же наш собственный класс — добавь св-во для доступа к экземпляру моковского репозитория внутри, и задавай ожидания теста снаружи. Т.е. нам требуется те же самые тесты "упаковать" в ожидаемый клиентом тип-оболочку. Тем паче, что каждый отдельный тест в общем случае задевает не всё АПИ класса (оно может быть большим), а лишь его некое его подможество. Вот его-то и достаточно нарисовать.

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


I>Повеселил так повеселил Ты заметил, что у тебя заюзаны чуть не все возможности дотнета что бы задетектить вызов двух методов ?


Тяжелый ты товарищ, однако... Еще и крайне невнимателен.

Два однострочных метода наблюдаются у твоего адаптера. И да, ты такой весь довольный написал код тестирования этих двух однострочных методов. Ну поздравляю. Только я решал другую задачу — изолирование нашего прикладного кода от ненашего. Чтобы иметь возможность написать тест, который ты написать, увы, не можешь. Да плевать, что в некоей конкретной задаче "код простой, тестировать не надо". Я на простом примере показываю, как тебе выкручиваться в сложных. Произвольный наследник внутри своей реализации может дергать доступный ему базовый ThirdParty произвольное кол-во раз по сколь угодно сложному протоколу. Даже если у наследника всего один метод.


I>Тоже неплохо — новое слово в поведенческом тестировании


Это в первую очередь юнит-тестирование, с требованием изолированного тестирования всех участников. Слово "всех" — такое же ключевое, как "изолирование".
А потом уже это поведенческое или какое еще. Не всегда нужны поведенческие тесты, но изолированность — всегда. Иначе непонятно, кого тестируем.

В общем, аргументы у тебя неплохи: целевой прикладной код лучше не тестировать, а то вишь, два лишних интерфейса ради такой ерунды появилось, или, О ужас!, заглушку писать придется... Браво! Профанация кучи потраченного здесь времени. Как же народ раньше писал себе заглушки десятилетиями, до появления мок-фреймворков? Сейчас непокрываемых моками сценариев максимум пару-другую процентов наберется, но ты уже заранее отказываешься их тестировать. Повторное браво!


I>С твоего позволения я запощу ссылку в Тестирование, шоб люди просветились.


Ради бога.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.