Языки с "Делегирование=Наследование"
От: Буравчик Россия  
Дата: 20.03.16 14:43
Оценка:
В языках программирования обычно наследование делается просто указанием нового и старого класса.
Этого достаточно, чтобы новый класс обладал методами старого класса.
class NewClass: OldClass


Делегирование очень часто является более подходящим (правильным) паттерном,
но в обычных языка делегирование становится многословным. Приходится явно описывать каждый делегируемый метод
class NewClass
   obj: OldClass
   method1(x) { obj.method1(x) }
   method2(x) { obj.method2(x) }    
   method3(x) { obj.method3(x) }


Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?
Best regards, Буравчик
Re: Языки с "Делегирование=Наследование"
От: Jack128  
Дата: 20.03.16 15:30
Оценка: 1 (1) +1
Здравствуйте, Буравчик, Вы писали:

Б>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


Delphi, kotlin
Re: Языки с "Делегирование=Наследование"
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.03.16 22:25
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


В Немерле есть макра делающая делегирование.

https://github.com/rsdn/nemerle/wiki/Design-patterns

Но на практике используется редко.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Языки с "Делегирование=Наследование"
От: anonymouse2 Иностранный Агент
Дата: 21.03.16 06:21
Оценка:
Здравствуйте, Jack128, Вы писали:

Б>>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?

J>Delphi, kotlin

Интересно... А на примерах покажете?
Нет такого преступления, на которое не пошло бы суверенное родоплеменное быдло ради продления своего бессмысленного рода и распространения своего бессмысленного генома.
Re[3]: Языки с "Делегирование=Наследование"
От: dr. Acula Украина  
Дата: 21.03.16 06:53
Оценка:
A>Интересно... А на примерах покажете?

class Example {
  var p: String by Delegate()
}
...
class Delegate {
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }
 
  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    println("$value has been assigned to '${property.name} in $thisRef.'")
  }
...
val e = Example()
println(e.p)

Example@33a17727, thank you for delegating ‘p’ to me!


тута
Re[2]: Языки с "Делегирование=Наследование"
От: Jack128  
Дата: 21.03.16 07:29
Оценка: 5 (2)
Здравствуйте, Jack128, Вы писали:

J>Здравствуйте, Буравчик, Вы писали:


Б>>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


J>Delphi, kotlin


delphi
type
  IWorker = interface
    procedure DoWork();
  end;
  TWorker = class(TInterfacedObject, IWorker)
    procedure DoWork();
  end;

  TBoss = class(TInterfacedObject, IWorker)
    property Subordinate: IWorker read GetSubordinate implements IWorker; //  босс делегирует реализацию IWorker подчиненному 
  end;


kotlin
interface IWorker {
  fun doWork()
}

class Worker() : IWorker {
  override fun doWork() {}
}

class Boss(val subordinate: IWorker) : IWorker by subordinate


ЗЫ
Тут http://rsdn.ru/forum/philosophy/6389711.1
Автор: dr. Acula
Дата: 21.03.16
написано не про делегирование реализации интерфейсов, а про делегирование свойств. Прикольная фича, но не по теме.
Re: Языки с "Делегирование=Наследование"
От: Sinix  
Дата: 21.03.16 09:33
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


Если понимать вопрос буквально, то я бы добавил яваскрипт
Re: Языки с "Делегирование=Наследование"
От: anonymous Россия http://denis.ibaev.name/
Дата: 22.03.16 07:03
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Делегирование очень часто является более подходящим (правильным) паттерном, но в обычных языка делегирование становится многословным. Приходится явно описывать каждый делегируемый метод

Б>
Б>class NewClass
Б>   obj: OldClass
Б>   method1(x) { obj.method1(x) }
Б>   method2(x) { obj.method2(x) }    
Б>   method3(x) { obj.method3(x) }
Б>


Не обязательно. Используя кодогенерацию можно реализовать более изящное решение. Например, в Perl с помощью фреймворков Moo(se) это будет выглядеть так (все нужные методы будут сгенерированы.):
package NewClass;

has obj => (
    is      => 'rw',
    isa     => InstanceOf[OldClass],
    handles => [qw(method1 method2 method3)],
);
Re: Языки с "Делегирование=Наследование"
От: FR  
Дата: 23.03.16 15:54
Оценка:
Здравствуйте, Буравчик, Вы писали:


Б>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


Язык D http://dlang.org/spec/class.html#AliasThis

struct Foo
{
    int baz = 4;
    int get() { return 7; }
}

class Bar
{
    Foo foo;
    alias foo this;
}

void main()
{
    auto bar = new Bar;
    int i = bar.baz; // i == 4
    i = bar.get(); // i == 7
}
Re: Языки с "Делегирование=Наследование"
От: Pzz Россия https://github.com/alexpevzner
Дата: 23.03.16 17:58
Оценка: 8 (1)
Здравствуйте, Буравчик, Вы писали:

Б>Вопрос. Есть ли языки, которые позволяют применить делегирование также элегантно (немногословно), как и наследование?


Go.

type Parent struct {
  a, b int
}

type Child {
  Parent
  c, f bool
}


Parent является в структуре Child анонимным полем.
Child наследует содержимое Parent'а и все его методы, если только не оверриайдит их собственными.
Re: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 25.03.16 22:55
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Делегирование очень часто является более подходящим (правильным) паттерном,


Это в каких же?

С моей точки зрения, делегирование по сути своей — весьма спорный паттерн и поэтому очень хорошо, что прописывать его надо явно.
Re[2]: Языки с "Делегирование=Наследование"
От: jazzer Россия Skype: enerjazzer
Дата: 26.03.16 02:45
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Здравствуйте, Буравчик, Вы писали:


Б>>Делегирование очень часто является более подходящим (правильным) паттерном,


BDA>Это в каких же?


BDA>С моей точки зрения, делегирование по сути своей — весьма спорный паттерн и поэтому очень хорошо, что прописывать его надо явно.


Если воспринимать его просто как сахар, то вполне нормально (и в том же С++ элементарно реализуется на макросах, кстати)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Языки с "Делегирование=Наследование"
От: AlexRK  
Дата: 26.03.16 06:48
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>С моей точки зрения, делегирование по сути своей — весьма спорный паттерн


А в чем именно он спорный? Мне всегда казалось, что в правильно реализованном делегировании нет мест, где можно случайно наломать дров.
Re[3]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 26.03.16 20:05
Оценка:
Здравствуйте, AlexRK, Вы писали:

BDA>>С моей точки зрения, делегирование по сути своей — весьма спорный паттерн

ARK>А в чем именно он спорный? Мне всегда казалось, что в правильно реализованном делегировании нет мест, где можно случайно наломать дров.

А вы приведите хоть один пример с правильным делегированием, его и разберем.
Re[4]: Языки с "Делегирование=Наследование"
От: AlexRK  
Дата: 27.03.16 15:59
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>>>С моей точки зрения, делегирование по сути своей — весьма спорный паттерн

ARK>>А в чем именно он спорный? Мне всегда казалось, что в правильно реализованном делегировании нет мест, где можно случайно наломать дров.

BDA>А вы приведите хоть один пример с правильным делегированием, его и разберем.


А без моего примера не можете пояснить свои слова про спорный паттерн?

Ну ок, вот выше был пример: http://rsdn.ru/forum/philosophy/6393576.1
Автор: Pzz
Дата: 23.03.16
Re[5]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 27.03.16 17:36
Оценка: -1
Здравствуйте, AlexRK, Вы писали:

ARK>А без моего примера не можете пояснить свои слова про спорный паттерн?


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

И как, полегчало? Не думаю.

Жизнь меня научила тому, что надо сразу переходить к примерам. Допустим, вы бы хотели возложить бремя придумывания примеров на меня. Есть одна проблемка: примеры чего, мне, человеку, которому паттерн кажется спорным, следует привести? Того, что он спорен? А вы на это скажете, что это мои примеры плохие (но есть хорошие).

Вот это все я мысленно опустил и предложил: давайте вы сразу приведете пример хорошего кода с делегированием. Где делегирование улучшает читаемость или мешает совершению ошибок. (Другие критерии я не думаю, что есть смысл рассматривать, но готов, если убедительно покажете). А я перепишу его без делегирования или признаю, что оно уместно. Так мы сможем быстро разобрать все случаи. К игре приглашаются все желающие.

ARK>Ну ок, вот выше был пример: http://rsdn.ru/forum/philosophy/6393576.1
Автор: Pzz
Дата: 23.03.16


И чего это пример? Бессмысленного и бесполезного учебного кода? Пожалуйста, давайте не будем замусоривать обсуждение такими примерами.
Отредактировано 27.03.2016 17:37 0BD11A0D . Предыдущая версия .
Re[6]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 29.03.16 10:08
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Вот это все я мысленно опустил и предложил: давайте вы сразу приведете пример хорошего кода с делегированием. Где делегирование улучшает читаемость или мешает совершению ошибок. (Другие критерии я не думаю, что есть смысл рассматривать, но готов, если убедительно покажете). А я перепишу его без делегирования или признаю, что оно уместно. Так мы сможем быстро разобрать все случаи. К игре приглашаются все желающие.


Возьмём старый добрый COM. И его ярчайших представителей — VB, Delphi, C++/ATL.
В васике наследования вообще не было. Так что переписывать на нём без делегирования просто не получится.
Компонентная модель вообще предполагает, что кастомизация поведения объектов происходит только через предоставление интерфейсов друг другу, а делегирование — просто немножко сахара над этим.

Чем делегирование лучше наследования:
Наследование создаёт россыпь интерфейсов толщиной в одну функцию (поскольку каждую функцию можно переопределить).
И эти интерфейсы доступны как наружу, так и внутри реализации — интерфейсы наследников к предку (механизм, который эксплуатируется в паттерне "шаблонный метод", например).
Для каждого интерфейса мысленно прописывается контракт. А если несколько функций выступают в связке, то этот контракт расползается, его легче нарушить.
При делегировании каждый класс отвечает за соблюдение своей части контракта.

Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.
Перекуём баги на фичи!
Re[7]: Языки с "Делегирование=Наследование"
От: vsb Казахстан  
Дата: 29.03.16 10:12
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.


Речь о ручном делегировании? Если язык поддерживает делегирование на уровне синтаксиса, почему оно дороже?
Re[8]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 29.03.16 10:58
Оценка:
Здравствуйте, vsb, Вы писали:

К>>Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.

vsb>Речь о ручном делегировании? Если язык поддерживает делегирование на уровне синтаксиса, почему оно дороже?

Синтаксис уменьшает писанину, но ненамного. В ATL делегирование интерфейса агрегату (и, одновременно, предоставление ему своих интерфейсов) займёт ровно одну строчку.
Плюс пару строк обвязки в коклассах главного объекта и агрегата, — но они так и этак добавятся.

А память и скорость — это почти неизбежная расплата.
Только если синтаксическими макросами сделать облако связанных объектов с минимальной косвенностью вызовов (немерле, шаблоны C++ — правда, именно шаблоны в нынешней редакции не могут сделать это бесплатно и изящно).
Хотя, если C++, препроцессор плюс CRTP, наверно, можно сделать инфраструктуру...
Перекуём баги на фичи!
Re[7]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 30.03.16 03:43
Оценка:
Здравствуйте, Кодт, Вы писали:

BDA>>Вот это все я мысленно опустил и предложил: давайте вы сразу приведете пример хорошего кода с делегированием. Где делегирование улучшает читаемость или мешает совершению ошибок. (Другие критерии я не думаю, что есть смысл рассматривать, но готов, если убедительно покажете). А я перепишу его без делегирования или признаю, что оно уместно. Так мы сможем быстро разобрать все случаи. К игре приглашаются все желающие.


К>Возьмём старый добрый COM. И его ярчайших представителей — VB, Delphi, C++/ATL.

К>В васике наследования вообще не было. Так что переписывать на нём без делегирования просто не получится.
К>Компонентная модель вообще предполагает, что кастомизация поведения объектов происходит только через предоставление интерфейсов друг другу, а делегирование — просто немножко сахара над этим.

Ну хорошо: если его нечем заменить (из языка убраны другие средства), тогда, конечно.

К>Чем делегирование лучше наследования:

К>Наследование создаёт россыпь интерфейсов толщиной в одну функцию (поскольку каждую функцию можно переопределить).
К>И эти интерфейсы доступны как наружу, так и внутри реализации — интерфейсы наследников к предку (механизм, который эксплуатируется в паттерне "шаблонный метод", например).
К>Для каждого интерфейса мысленно прописывается контракт. А если несколько функций выступают в связке, то этот контракт расползается, его легче нарушить.
К>При делегировании каждый класс отвечает за соблюдение своей части контракта.

К>Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.


А вот это хотелось бы в виде кода, поскольку там вероятны какие-то проблемы с декомпозицией.
Re[9]: Языки с "Делегирование=Наследование"
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 30.03.16 10:56
Оценка:
Здравствуйте, Кодт, Вы писали:

vsb>>Речь о ручном делегировании? Если язык поддерживает делегирование на уровне синтаксиса, почему оно дороже?


К>Синтаксис уменьшает писанину, но ненамного. В ATL делегирование интерфейса агрегату (и, одновременно, предоставление ему своих интерфейсов) займёт ровно одну строчку.

К>Плюс пару строк обвязки в коклассах главного объекта и агрегата, — но они так и этак добавятся.

В каком-нибудь Go это тоже одна строчка, без оговорок на "обвязку".
В D примерно две строчки.
Re[8]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 30.03.16 11:05
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

К>>Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.

BDA>А вот это хотелось бы в виде кода, поскольку там вероятны какие-то проблемы с декомпозицией.

Данные:
Наследник содержит данные свои и предков в одном блоке памяти. Также там содержатся один или несколько vfptr'ов, по количеству интерфейсов и базовых классов.
Данные делегата живут в отдельном блоке. (Исключение — C++, где делегата можно добавить прямо в структуру владеющего объекта).
У владельца есть указатели (за исключением) на делегатов. У делегатов — есть vfptr'ы их собственных интерфейсов, плюс, скорее всего, указатели на владельца (и вот тут уже исключение сделать не получится). На ровном месте получили в 3 раза больше указателей!

Статический вызов метода: при наследовании — сместить базу (если надо) и вызвать функцию; при делегировании — взять смещение поля делегата, разыменовать (за исключением, см.выше), вызвать.

Динамический вызов:
при наследовании —
— разыменовать vfptr (обычно он под нулевым смещением),
— взять адрес функции в vtable и вызвать,
— если наследование со смещением базы, — то в vtable находится промежуточная функция, которая сместит базу и перейдёт к функции предка
при делегировании —
— взять смещение поля делегата,
— разыменовать,
— разыменовать его vfptr (у нас ведь всё равно есть наследование интерфейсов),
— взять адрес функции в vtable и вызвать (одна радость, там не будет кода по смещению базы)

Обратный вызов, из базы в наследника, из делегата во владельца
при наследовании —
— взять vfptr, далее понятно
при делегировании —
— взять поле указателя на владельца, разыменовать... далее понятно

Ещё один важный момент.
При наследовании — мы "из коробки" получаем объект, внешне выглядящий монолитно.
class Composite; .....
struct IA; struct IB; // неважно, как они там раскиданы по базам

Composite* c;
IA* a = dynamic_cast<IA>(c); // иногда и static_cast'а достаточно, ну неважно
IB* b = dynamic_cast<IB>(a);
IA* a2 = dynamic_cast<IA>(b);
assert(a == a2);

При делегировании — надо специально позаботиться, чтобы делегат мог вернуться к владельцу. Иначе это будет дорога в тупик.
Это можно делать руками в каждом конкретном случае, или руками на уровне инфраструктуры (пример из COM — специальные опции при создании CComObject и реализация GetControllingIUnknown), или компилятором — понапихать служебных указателей в каждый потенциально-делегируемый класс.
Перекуём баги на фичи!
Re[9]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 30.03.16 13:22
Оценка:
Здравствуйте, Кодт, Вы писали:

  Скрытый текст
К>>>Чем делегирование хуже: оно дороже по памяти, по скорости, ну и по писанине тоже.
BDA>>А вот это хотелось бы в виде кода, поскольку там вероятны какие-то проблемы с декомпозицией.

К>Данные:

К>Наследник содержит данные свои и предков в одном блоке памяти. Также там содержатся один или несколько vfptr'ов, по количеству интерфейсов и базовых классов.
К>Данные делегата живут в отдельном блоке. (Исключение — C++, где делегата можно добавить прямо в структуру владеющего объекта).
К>У владельца есть указатели (за исключением) на делегатов. У делегатов — есть vfptr'ы их собственных интерфейсов, плюс, скорее всего, указатели на владельца (и вот тут уже исключение сделать не получится). На ровном месте получили в 3 раза больше указателей!

К>Статический вызов метода: при наследовании — сместить базу (если надо) и вызвать функцию; при делегировании — взять смещение поля делегата, разыменовать (за исключением, см.выше), вызвать.


К>Динамический вызов:

К>при наследовании -
К>- разыменовать vfptr (обычно он под нулевым смещением),
К>- взять адрес функции в vtable и вызвать,
К>- если наследование со смещением базы, — то в vtable находится промежуточная функция, которая сместит базу и перейдёт к функции предка
К>при делегировании -
К>- взять смещение поля делегата,
К>- разыменовать,
К>- разыменовать его vfptr (у нас ведь всё равно есть наследование интерфейсов),
К>- взять адрес функции в vtable и вызвать (одна радость, там не будет кода по смещению базы)

К>Обратный вызов, из базы в наследника, из делегата во владельца

К>при наследовании -
К>- взять vfptr, далее понятно
К>при делегировании -
К>- взять поле указателя на владельца, разыменовать... далее понятно

К>Ещё один важный момент.

К>При наследовании — мы "из коробки" получаем объект, внешне выглядящий монолитно.
К>
К>class Composite; .....
К>struct IA; struct IB; // неважно, как они там раскиданы по базам

К>Composite* c;
К>IA* a = dynamic_cast<IA>(c); // иногда и static_cast'а достаточно, ну неважно
К>IB* b = dynamic_cast<IB>(a);
К>IA* a2 = dynamic_cast<IA>(b);
К>assert(a == a2);
К>

К>При делегировании — надо специально позаботиться, чтобы делегат мог вернуться к владельцу. Иначе это будет дорога в тупик.
К>Это можно делать руками в каждом конкретном случае, или руками на уровне инфраструктуры (пример из COM — специальные опции при создании CComObject и реализация GetControllingIUnknown), или компилятором — понапихать служебных указателей в каждый потенциально-делегируемый класс.


Я очень сильно извиняюсь, что заставил столько текста написать, но я имел в виду совсем другое. Где промышленный код, в котором все эти проблемы заложены? С размытиями контрактов и т.д.

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

Например, проект, написанный мною вчера вечером, когда я узнал, что мой кухонный компьютер не уходит в sleep из-за того, что в Windows 7 какие-то умники запретили сон при наличии открытых файлов из сетевой шары. То есть, вывел его с утра из сна пультом, нажал Play, тем самым открыв плейлист с файлами из сетевого хранилища, позавтракал под музыку, ушел из дома, вернулся вечером, а он весь день, оказывается, жарил стенку, потому, что — шара же. Сначала я, как водится, почитал доку. Оказалось, что есть powercfg /requests, который показывает, почему не произошло sleep'а. Потом стал смотреть, как отменить такое поведение. Оказалось, надо в реестре нашаманить, чтобы только опция в GUI появилась. Настроил опцию — не работает. Ну хорошо, написал код, который форсит сон, заодно повесил выключение на пультовую кнопку.

  Скрытый текст
#include "stdafx.h"
#include "SleepTool.h"

#include "Commctrl.h"
#include <PowrProf.h>

// Global Variables:
HINSTANCE hInst;                                // current instance

bool IsMPCPlaying()
{
    HWND hwndMPC = ::FindWindow(L"MediaPlayerClassicW", NULL);
    if (!hwndMPC)
        return false;

    HWND hwndStatusbar = ::FindWindowEx(hwndMPC, NULL, L"#32770", NULL);
    if (!hwndStatusbar)
    {
        ::MessageBox(NULL, L"MPC Status Bar not found", L"SleepTool", MB_OK);
        return false;
    }

    HWND hwndLabel = ::FindWindowEx(hwndStatusbar, NULL, L"Static", L"Playing");
    return hwndLabel != NULL;
}

LRESULT CALLBACK KbdLL(int code, WPARAM wParam, LPARAM lParam)
{
    if (code == HC_ACTION)
    {
        KBDLLHOOKSTRUCT *khs = (KBDLLHOOKSTRUCT*)lParam;
        if (khs->vkCode == VK_BROWSER_HOME)
        {
            if (wParam == WM_KEYUP)
            {
                if (!::SetSuspendState(FALSE, TRUE, FALSE))
                {
                    ::MessageBox(NULL, L"SetSuspendState failed.", L"SleepTool", MB_OK);
                }
            }
            return -1;
        }
    }
    return CallNextHookEx(nullptr, code, wParam, lParam);
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    HHOOK hKbdLL = ::SetWindowsHookEx(WH_KEYBOARD_LL, KbdLL, hInstance, 0);

    ::SetTimer(NULL, 1, 60 * 1000, NULL);

    MSG msg = {0};
    while (::GetMessage(&msg, NULL, 0, 0) != 0)
    {
        if (msg.message == WM_TIMER)
        {
            LASTINPUTINFO lii;
            lii.cbSize = sizeof(lii);
            ::GetLastInputInfo(&lii);

            DWORD dwLastEvent = ::GetTickCount() - lii.dwTime;
            if (dwLastEvent > 30 * 60 * 1000)
            {
                bool bPlaying = IsMPCPlaying();
                if (!bPlaying)
                {
                    // Sleep.
                    if (!::SetSuspendState(FALSE, TRUE, FALSE))
                    {
                        ::MessageBox(NULL, L"SetSuspendState failed.", L"SleepTool", MB_OK);
                    }
                }
            }
        }
    }

    ::KillTimer(NULL, 1);

    ::UnhookWindowsHookEx(hKbdLL);

    return 0;
}


Бывают проекты сильно, очень сильно, сложнее в плане функционала, которые годами пишутся несколькими людьми, но они недалеко ушли по структуре от «скрипта», показанного выше. Их GUI (самое ООшное место обычно) вынесен в HTML и полностью отвязывается от т.н. «бизнес»-логики, а сама эта «бизнес»-логика объективно оказывается набором действий. Добавление классов не улучшает читаемость, а только все усложнит (как усложнило бы все добавление класса PowerManager в примере выше — совершенно искуственная сущность). Конечно, чужие классы при этом используются активно, например, строки и контейнеры.

Это, напомню, то, что я вижу чаще всего.

Реже я встречаю классы. Например, вот такой проектик: есть база данных, с которой работает администратор. Администратор создает таблицы для простых пользователей, а потом добавляет разные автоматизирующие штуки. Например, если в одной таблице подставлена ссылка на справочник, то при изменении в справочнике изменения копируются в исходную таблицу. Получается реляционность, прилепленная к чужой программе типа Excel'а. Или, скажем, суммирование. Если одна таблица содержит колонку, которую можно просуммировать, сумма должна автоматически обновляться во всех таблицах, куда она включена (как сумма).

В мануале администратора написано: вы можете автоматизировать работу пользователей вашего, условно говоря, Excel'а, поставив нашу утилиту и создав специальную таблицу с правилами. Если вы хотите реляционность — укажите тип правила «Реляционность». Хотите суммирование — укажите тип правила «Суммирование». Хотите, чтоб при изменениях юзеру в мессенджер приходило извещение, или генерировался документ на каждую запись, или еще чего-нибудь — укажите соответствующий тип правила. Для каждого правила, независимо от типа, все равно нужно заполнить колонки «Таблица-источник», «Колонка-источник», «Целевая таблица», «Целевая колонка».

Это естественнейшим образом ложится на базовый класс Rule, который, например, считывает источники и таргеты, и на производные классы LookupRule, SumTotalRule и так далее.

Когда новый программист читает мануал администратора (то есть, пользователя проекта), он напитывается понятиями этого самого пользователя («правила администратора», «источники», «цели»), а потом смотрит в код и видит результат декомпозиции: класс Rule, дочерние классы LookupRule, SumTotalRule, строковые поля SourceColumn, SourceTable и так далее.

  Скрытый текст
internal abstract class Rule
    {
        [Bottleneck]
        internal Rule();

        #region Abstract/virtual methods, implemented/overriden in derivatives.

        [RuleCaching]
        protected abstract bool CheckCompatibility(SourceTargetPair pair);

        [RuleCaching]
        protected abstract SourceTargetPair[] EnumerateFields();

        [Bottleneck]
        protected abstract void ExecuteOnSourceChanged(ItemsWere sourceItems, string itemGuid);

        [Bottleneck]
        internal virtual string GetRequiredFields();

        #endregion

        #region Public interface.

        [Bottleneck]
        internal void Initialize(SqlConnection connection, bool isUpdated, string title, string ruleId,
                                string ruleList, string ruleWeb, string text, string formula,
                                string sourceListFullName, string targetListFullName, string sourceFields,
                                string targetFields, string changedItemsCondition, string allItemsCondition);

        [Bottleneck]
        internal static Rule GenerateRule(string ruleType); // Class factory.

        [Bottleneck]
        internal void PrepareForExecution(bool skip, string query);

        [Bottleneck]
        internal virtual string GetSelectSourceListItemsQuery(TriggerAction triggerAction, string requiredFields = null);

        internal void ExecuteOnSourceChanged();

        #endregion

        #region Helpers for internal interface implementation.

        [Bottleneck]
        private static RuleType ParseRuleType(string ruleType);

        [RuleCaching]
        private void SaveRule(bool valid);

        [RuleCaching]
        private void ConvertCondition(IDictionary<string, ColumnType> sourceColumns,
                                        IEnumerable<Field> fields,
                                        out string allItemsCondition, out string changedItemsCondition);

        #endregion

        #region Service for derivative classes.

        [RuleCaching]
        protected static string CastColumnOrValue(string columnOrValue, ColumnType columnType,
                                                    string table = "");

        [Bottleneck]
        protected static void AddTargetValue(ref int parameter, ref string assignments,
                                                AssignTo assignTo, List<SqlParameter> parameters,
                                                string targetColumn, ColumnType targetColumnType,
                                                object targetValue);

        #endregion

        #region Data.

        #endregion

        #region Properties.

        #endregion
    }

...

class SumTotalRule : LookupRule
    {
        private static List<string> ValidAggregationFunctions = new List<string>()
        {
            "avg",
            "sum",
            "max",
            "min",
            "count"
        };

        [Bottleneck]
        internal override string GetRequiredFields();

        [Bottleneck]
        internal override string GetSelectSourceListItemsQuery(TriggerAction triggerAction,
                                                                string requiredFields = null);

        [Bottleneck]
        protected override void ExecuteOnSourceChanged(ItemsWere sourceItems, string itemGuid);

        [RuleCaching]
        protected override bool CheckCompatibility(SourceTargetPair pair);
    }


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

***

Дело в том, что программирование вообще — очень простое занятие, если специально не усложнять. Чем программисты любят заниматься, либо чтоб не уволили, либо из естественного любопытства, не добавляя ценности. Если думать над ценностью, грамотно проводить декомпозицию, код со всей его структурой будет очень прост. Я просто не могу представить себе, какова должна быть предметная область, чтобы делегирование в коде ее адекватно передавало и было способом упростить чтение кода. Я и прошу: может кто-нибудь привести пример?

***

Что касается ручной возни с vtbl'ом, я вообще не понимаю, что это и зачем. Знать про vtbl полезно, если ты компиляторы пишешь. Или если нечаянно испортил память, найти место, где ломаешь вызовы и крэшишь все. Оптимизация, когда расходы на виртуальные вызовы важны? Бывает крайне редко, но тогда пиши на голом C с хендлами. Здесь о другом, здесь об организации кода.
Re[4]: Языки с "Делегирование=Наследование"
От: Буравчик Россия  
Дата: 30.03.16 15:00
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

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


BDA>>>С моей точки зрения, делегирование по сути своей — весьма спорный паттерн

ARK>>А в чем именно он спорный? Мне всегда казалось, что в правильно реализованном делегировании нет мест, где можно случайно наломать дров.

BDA>А вы приведите хоть один пример с правильным делегированием, его и разберем.


Ну вообще паттерн делегирования повсюду.

См. Шаблон делегирования, структурные шаблоны проектирования

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

Если же говорить не про делегирование вообще, а про "делегирование vs наследование", то вот пример: имеешь реализованную структуру "список", надо построить "стек". Можно конечно унаследовать стек от списка, да вот только стек это не список. Лучше спрятать список внутри реализации, а некоторые команды стека делегировать списку.
Best regards, Буравчик
Re[5]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 30.03.16 16:53
Оценка:
Здравствуйте, Буравчик, Вы писали:

BDA>>А вы приведите хоть один пример с правильным делегированием, его и разберем.

Б>Если же говорить не про делегирование вообще, а про "делегирование vs наследование", то вот пример: имеешь реализованную структуру "список", надо построить "стек". Можно конечно унаследовать стек от списка, да вот только стек это не список. Лучше спрятать список внутри реализации, а некоторые команды стека делегировать списку.

Вот нет бы сразу код написать.

1. Какой интерфейс у списка, а какой у стека? С моей точки зрения, список должен иметь Add(), Insert(), Remove(), а стек — Push() и Pop(). Даже в STL, который образец нечитаемого кода, в std::vector метод называется push_back(), а в std::stack — просто push().

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

2. Допустим, немногословность подразумевает явный маппинг одинаковых сигнатур. Заодно решается проблема со скрытием ненужной части аггрегированного класса в делегирующем классе. Тогда замапив push() на push_back(), pop() на pop_back(), а top() на back() вы получите искомое. Проблема в том, что:

а. В классе списка вам надо таскать back() или first(), или top() или еще что-то, нужное только для этой цели. Потому, что иначе гораздо проще вызвать list[0] или list[list.size — 1]. Ненужный метод усложняет чтение. Ни в FCL, ни в Java такого изврата нет. Поэтому я и назвал STL'ный код нечитаемым.
б. Возможна только та реализация стека, в которой важно быстрое добавление/удаление элементов, а не быстрая выгрузка списка, поскольку он перевернутый.

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

Можно сказать, что top() надо реализовать как { return list[0]; }, но тогда что тут от делегирования остается? Так, извините, любое поле строкового класса добавил — уже делегировал обработку текста в string. В любом случае, даже если мы считаем это делегированием, о чем тогда ваш вопрос? Многословный синтаксис у вас уже есть. А про него я написал:

очень хорошо, что прописывать его надо явно.

Re[6]: Языки с "Делегирование=Наследование"
От: Буравчик Россия  
Дата: 30.03.16 19:45
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Здравствуйте, Буравчик, Вы писали:


BDA>Можно сказать, что top() надо реализовать как { return list[0]; }, но тогда что тут от делегирования остается? Так, извините, любое поле строкового класса добавил — уже делегировал обработку текста в string. В любом случае, даже если мы считаем это делегированием, о чем тогда ваш вопрос? Многословный синтаксис у вас уже есть. А про него я написал:


Согласен, не очень удачный пример про список и стек. И, действительно, вопрос был навеян именно нежеланием писать методы типа
method1(x,y,z) { obj.method1(x,y,z) }

Т.е. чисто делегирующие методы, в которых сигнатуры совпадают.

Но в целом, интересует вопрос шире, насколько в современных языках делегирование явно выделяется (как некая конструкция языка). Что-то типа
method1, method2, method3 delegated_to obj
или
method1 delegated_to obj.goodmethod1

И дает ли это языку какие-то особые возможности.
Best regards, Буравчик
Re[7]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 30.03.16 20:55
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Согласен, не очень удачный пример про список и стек.


А есть ли более удачный? Представьте любой, самый волшебный синтаксис. На какой задаче вы его будете применять?
Re[10]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 31.03.16 09:34
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Я очень сильно извиняюсь, что заставил столько текста написать, но я имел в виду совсем другое. Где промышленный код, в котором все эти проблемы заложены? С размытиями контрактов и т.д.


Вы там в Москве совсем офигели, ламборгини за 300 миллионов рублей! А у нас в Рязани гречка по 70 рублей за кило.
(Кстати, в пересчёте на массу ламборгини — порядка 100 тысяч, сравните с 300 миллионами).


BDA>Дело в том, что программирование вообще — очень простое занятие, если специально не усложнять. Чем программисты любят заниматься, либо чтоб не уволили, либо из естественного любопытства, не добавляя ценности. Если думать над ценностью, грамотно проводить декомпозицию, код со всей его структурой будет очень прост. Я просто не могу представить себе, какова должна быть предметная область, чтобы делегирование в коде ее адекватно передавало и было способом упростить чтение кода. Я и прошу: может кто-нибудь привести пример?


Какова предметная область, чтобы делегирование адекватно передавало? Да ты прямо сейчас её смотришь!
Оконная система. Не мега-наследник от HWND реализовал всю эту красоту, "как нарисовать и как реагировать на мышь в каждой конкретной точке экрана" (если помыслить Шейнфинкелем-Карри, то у экрана есть 1920*1080 маленьких интерфейсиков), а десктоп делегировал это окнам приложений, окна приложений — дочерним окнам, и т.д.

BDA>Что касается ручной возни с vtbl'ом, я вообще не понимаю, что это и зачем. Знать про vtbl полезно, если ты компиляторы пишешь. Или если нечаянно испортил память, найти место, где ломаешь вызовы и крэшишь все. Оптимизация, когда расходы на виртуальные вызовы важны? Бывает крайне редко, но тогда пиши на голом C с хендлами. Здесь о другом, здесь об организации кода.


Спросил про цену вопроса, я ответил. Делегирование объективно жрёт больше памяти и времени. Кому это критично, должны это учитывать, а кому некритично, зачем спрашиваете.
Перекуём баги на фичи!
Re[11]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 31.03.16 11:34
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Какова предметная область, чтобы делегирование адекватно передавало? Да ты прямо сейчас её смотришь!

К>Оконная система. Не мега-наследник от HWND реализовал всю эту красоту, "как нарисовать и как реагировать на мышь в каждой конкретной точке экрана" (если помыслить Шейнфинкелем-Карри, то у экрана есть 1920*1080 маленьких интерфейсиков), а десктоп делегировал это окнам приложений, окна приложений — дочерним окнам, и т.д.

Вы передергиваете, надеюсь, что несознательно.

Во-первых, делегирование понимается и как консультирование, обычно в форме аггрегирования (оригинальная трактовка), и (после статьи Либермана) как языковой механизм диспетчеризации. Топикстартер своим вопросом однозначно сузил это понятие до второго значения. На самом деле, даже больше, поскольку его диспетчеризация — исключительно compile-time. Мой вопрос, зачем ему это нужно, был в том же контексте. Он вскользь написал, что сам не знает, дает ли это какие-то возможности. Похоже, действительно нахрен это не надо, раз никто не смог ни одного примера привести.

Во-вторых, в любом из значений делегирование относится к ООП, в то время как API «оконной системы с HWND», да и сама ее реализация — процедурно-хендловые.
Re[8]: Языки с "Делегирование=Наследование"
От: AlexRK  
Дата: 31.03.16 13:52
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

Б>>Согласен, не очень удачный пример про список и стек.


BDA>А есть ли более удачный? Представьте любой, самый волшебный синтаксис. На какой задаче вы его будете применять?


В тех же задачах, где применяется наследование. Только получившийся объект не будет автоматически совместим по присваиванию с делегатом (только в том случае, если оба объекта реализуют общие интерфейсы). А наследование — на помойку.
Re[12]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 31.03.16 13:55
Оценка:
Здравствуйте, 0BD11A0D, Вы писали:

BDA>Вы передергиваете, надеюсь, что несознательно.


Это был ответный удар. Потому что сводить программирование к написанию скриптиков и клиентов БД — толсто.

BDA>Во-первых, делегирование понимается и как консультирование, обычно в форме аггрегирования (оригинальная трактовка), и (после статьи Либермана) как языковой механизм диспетчеризации. Топикстартер своим вопросом однозначно сузил это понятие до второго значения. На самом деле, даже больше, поскольку его диспетчеризация — исключительно compile-time. Мой вопрос, зачем ему это нужно, был в том же контексте. Он вскользь написал, что сам не знает, дает ли это какие-то возможности. Похоже, действительно нахрен это не надо, раз никто не смог ни одного примера привести.


Это не "нахрен не надо", а потому что про классовое ООП написана тьма монографий (начиная с Гради Буча и Гаммы-et-al), и народ следует этим идеям по нужде и без нужды. Поэтому и мыслить в этой парадигме легко, и примеров готовых полно.
А компонентное ООП не является таким мейнстримом. И даже ФП не является. Хотя они все взаимозаменяемы и выразимы друг через друга.
(Зато говнокодное ад-хок-ООП по образцу — является мейнстримом, потому что есть в каждом браузере).

BDA>Во-вторых, в любом из значений делегирование относится к ООП, в то время как API «оконной системы с HWND», да и сама ее реализация — процедурно-хендловые.


В мире полным-полно оконных систем, где ООП самое что ни есть классово правильное. То, что в винде оно замаскировано в угоду чисто-сишному API, чтобы кто угодно на чём угодно мог обращаться, без расизма "только ObjC, только какава с чаем" или "только C++/Qt", или "только смолток, только весёлые 60-е" — это даже не детали реализации, а мелочи. Так-то там и объекты, и классы всё равно есть. Плюс ещё надстройки в виде COM и WPF.

Кстати про COM. Я уже говорил, что эта компонентная модель недружественна к наследованию, зато дружественна к делегированию. (Собственно, любая компонентная модель имеет смещение баланса в эту сторону).
Хочешь примеров, возьми любую программу на васике.
Перекуём баги на фичи!
Re[13]: Языки с "Делегирование=Наследование"
От: 0BD11A0D  
Дата: 31.03.16 14:30
Оценка: -1
Здравствуйте, Кодт, Вы писали:

К>Это был ответный удар. Потому что сводить программирование к написанию скриптиков и клиентов БД — толсто.


Тут, оказывается, сражение какое-то. Я-то простодушно думал, мы разбираем вопрос нужности паттерна. Ладно, сдаюсь.
Re[9]: Языки с "Делегирование=Наследование"
От: Evgeny.Panasyuk Россия  
Дата: 31.03.16 14:34
Оценка:
Здравствуйте, AlexRK, Вы писали:

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


То есть private наследование?
#include <vector>

using namespace std;

struct custom : private vector<int>
{
    using vector<int>::push_back;
};

void func(const vector<int>&);

int main()
{
    custom x;
    x.push_back(1);
    func(x);
}

LIVE DEMO
main.cpp: In function 'int main()':
main.cpp:16:11: error: 'std::vector<int>' is an inaccessible base of 'custom'
     func(x);
           ^
Re[10]: Языки с "Делегирование=Наследование"
От: AlexRK  
Дата: 31.03.16 14:39
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

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


EP>То есть private наследование?


Да, но с точностью до метода (при необходимости).
Re[11]: Языки с "Делегирование=Наследование"
От: Evgeny.Panasyuk Россия  
Дата: 31.03.16 14:45
Оценка:
Здравствуйте, AlexRK, Вы писали:

EP>>То есть private наследование?

ARK>Да, но с точностью до метода (при необходимости).

В примере выше именно с точностью до метода:
struct custom : private vector<int>
{
    using vector<int>::push_back;
};
Re[7]: Языки с "Делегирование=Наследование"
От: Кодт Россия  
Дата: 31.03.16 15:22
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Но в целом, интересует вопрос шире, насколько в современных языках делегирование явно выделяется (как некая конструкция языка).

Б>И дает ли это языку какие-то особые возможности.

Идиома пимпл — даёт уменьшение зависимостей. К сожалению, она не оформлена как часть языка С++, чтобы пользователя избавить от писанины, переложив домысливание на плечи компилятора.

Сама по себе фича "пробрасывать интерфейсы не только между базой и наследником" не так уж и плоха.
Как и любая другая фича, — если она дешёвая, её будут популяризовывать, если нет — будут избегать.
На языке Си вполне можно писать в духе классового ООП, но — руками или препроцессором CFront. Да ну в топку, эти классы. А тем более, это наследование.
Перекуём баги на фичи!
Re[12]: Языки с "Делегирование=Наследование"
От: AlexRK  
Дата: 31.03.16 16:09
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>>>То есть private наследование?

ARK>>Да, но с точностью до метода (при необходимости).
EP>В примере выше именно с точностью до метода:

А, пардон, затупил. Тогда да, это оно. Если, конечно, еще есть варианты, как разрулить конфликты при делегировании групп методов от нескольких объектов.

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