Re[2]: паранойя наследования
От: Plague Россия  
Дата: 09.07.08 08:24
Оценка:
Здравствуйте, LaptevVV, Вы писали:
LVV>Ну почему... У меня вот обратная картина: привык мыслить функционально... ОО-решение приходит только во вторую очередь, да и то, хорошенько подумать нужно...
Мне чем нравится наследование — это уменьшение дублирования кода. Естественно, это не значит, что надо наследоваться от чего попадя. Надо все хорошенько продумать. При этом статическая особенность наследования относительно композиции, подобно статической и динамической типизации, и для С++ является более логичным. (опустим флем про минусы С++, о которых и так много сказано)
LVV>Вот писал интерпретатор виртуальной машины... Сходу написал все на функциях... Основной цикл процессора вызывает нужную функцию по указателю из массива указателей. А код операции является индексом.
LVV>А потом только подумал, что можно было определить абстрактный класс Команда и наследоваться от него для реализации каждой команды.
LVV>Пока реализовал промежуточное решение: разделил все на 2 дружественных класса: класс-процессор и класс-система команд. Инкапсулировал все функции-команды в класс система-команд как статические. Дальше буду реализовывать нормальный ОО-подход.

Писал я как-то эмулятор для ICFP-2006 UM машины... =) написал, замечательно... увидел реализацию с JIT. попытался обогнать хитрым исполнением — упирается в предсказание ветвлений проца, т.к. при выборке из массива все предсказания идут лесом. проигрыш — гигантский!

LVV>Это я опять к топику "что плохого в С++". Сильно много свободы. Одну и ту же работу — в трех разных видах делаю. Правда плюс есть — можно сравнивать подходы на практике. Но это только мне как преподу и пригодиться. А в реальном программировании сравнивать некогда — "копать" надо...

LVV>Вот и получается, что в большом проекте каждый пишет как привык, а не так, как нужно...
Это да, но я не считаю, что от наследования надо отказываться... не надо вдаваться в крайности!
А то получается, что ради использования или НЕ использования какой-то вещи, могут пожертвовать даже тем, ради чего это все замышлялось...

P.S. А вот пример генерации кода для инструкций UM машины, создается около 3.5к функций для каждой инструкции.
Делалось под VS6 компилятор, но должно компилится и на остальных. Из-за отсутствия частичной специализации пришлось идти на стандартные ухищрения, а так было бы меньше кода. Компилятору надо указать, чтоб памяти кушал побольше.
VS7.0 компилится не хочет — падает по INTERNAL COMPILER ERROR. Но компилятся должно 100%.
//#include <stdio.h>
//#include <stdlib.h>

unsigned long R[8];
unsigned long *IP;

template<int N>
struct OP {
    enum{RA = (N>>6)&7,RB = (N>>3)&7,RC = N&7};
    enum{RSA = N&7};
};

template<class T>
struct Array:public T {
    static void(*instr[T::SIZE])();
};

template<class T>
void(*Array<T>::instr[])();

template<class T, int N>
struct Rpt {
    static void RepeatN() {
        Array<T>::instr[N-1]=T::RealOp<N-1>::run;
        RptN<T, N-1>::RepeatN();
    }
};

template<class T>
struct Rpt0 {
    static void RepeatN(){}
};

template<int N>
struct RepeatTraits {
    template<class T>
    struct OPs {
        typedef Rpt<T, N> Base;
    };
};

template<>
struct RepeatTraits<0> {
    template<class T>
    struct OPs {
        typedef Rpt0<T> Base;
    };
};

template<class T, int N>
struct RptN : public RepeatTraits<N>::template OPs<T>::Base {};

typedef void(*fptr)();

template<class T>
struct OpEnv {
    static void gen() {
        RptN<T,T::SIZE>::RepeatN();
    }
    static fptr getFunction(unsigned long ID){
        return Array<T>::instr[ID&0x1FF];
    }
};

#define DECLARE_OPCODE(classname, code, asize, ccode) \
struct classname: public OpEnv<classname> {\
    enum {SIZE = asize, CODE = code};\
    template<int N>\
    struct RealOp: public OP<N>    {\
        static inline void run() {\
            ccode;\
        }\
    };\
    static char* name(){return #classname;}\
}

DECLARE_OPCODE(CMOV, 0, 512, if(R[RC]) R[RA] = R[RB] );
DECLARE_OPCODE(LOAD, 1, 512, R[RA] = ((unsigned long *)R[RB])[R[RC]] );
DECLARE_OPCODE(SAVE, 2, 512, ((unsigned long *)R[RA])[R[RB]] = R[RC] );
DECLARE_OPCODE(ADD , 3, 512, R[RA] = R[RB] + R[RC] );
DECLARE_OPCODE(MUL , 4, 512, R[RA] = R[RB] * R[RC] );
DECLARE_OPCODE(DIV , 5, 512, R[RA] = R[RB] / R[RC] );
DECLARE_OPCODE(ZAND, 6, 512, R[RA] = ~(R[RB] & R[RC]) );
DECLARE_OPCODE(HALT, 7, 1, exit(1) );
// alloc
// free
DECLARE_OPCODE(OUTP,10, 8, putchar(R[RC]&0xFF) );
DECLARE_OPCODE(INP ,11, 8, R[RC] = getchar() );
// jump
DECLARE_OPCODE(MOVD,13, 8, R[RSA] = *IP & 0x1FFFFFF; );

void main() {
        CMOV::gen();
        LOAD::gen();
        SAVE::gen();
        ADD::gen();
        MUL::gen();
        DIV::gen();
        ZAND::gen();
        HALT::gen();

        
        OUTP::gen();
        INP::gen();

        MOVD::gen();
}
... << RSDN@Home 1.2.0 alpha rev. 787>>
Re[3]: паранойя наследования
От: igna Россия  
Дата: 09.07.08 08:46
Оценка: -1 :)
Здравствуйте, Plague, Вы писали:

P>При этом статическая особенность наследования относительно композиции, подобно статической и динамической типизации, и для С++ является более логичным.


Композиция может быть статической.

Если статическая композиция достаточна, не стоит использовать наследование только для того, чтобы сэкономить на написании forwarding functions.
Re[13]: паранойя наследования
От: rsn81 Россия http://rsn81.wordpress.com
Дата: 09.07.08 08:50
Оценка:
Здравствуйте, WFrag, Вы писали:

[skipped]
WF>Копируем туда B.class с первого каталога и запускаем:
[skipped]
В контексте обсуждения... вы это все серьезно?

WF>Как видишь, у класса B магически подменился предок безо всяких перекомпиляций, причём у этого предка даже метода одного не хватает.

Ню-ню.

WF>О какой фиксации идёт речь? Что именно фиксируется-то?

Вы сами ответили на свои вопросы, смотрите:
WF>В случае Java по большому счету отличие лишь в том, что в случае наследования вызов некоторого функционала делается на параметре this (неявный параметр при вызове методов), а в случае композиции -- на некотором поле (т.е сначала получаем значение поля, потом делаем вызов).
Или тоже самое другими словами:
Re[11]: паранойя наследования
Автор: rsn81
Дата: 09.07.08

Re[13]: паранойя наследования
Автор: rsn81
Дата: 09.07.08

Кстати, не знаю, чего вы так прицепились к Java... это не только там так.
Re[14]: паранойя наследования
От: WFrag США  
Дата: 09.07.08 09:00
Оценка:
Здравствуйте, rsn81, Вы писали:

R>[skipped]

WF>>Копируем туда B.class с первого каталога и запускаем:
R>[skipped]
R>В контексте обсуждения... вы это все серьезно?

Легко. Компилировали с одной версией библиотеки, запустили с другой. В Java это сплошь и рядом.

Это просто пример, что никакой особой "фиксации" не происходит. Вся разница — в отсутствии лишней косвенности и всё.

WF>>Как видишь, у класса B магически подменился предок безо всяких перекомпиляций, причём у этого предка даже метода одного не хватает.

R>Ню-ню.

Что ню-ню? B-то не перекомпилировали.

R>Или тоже самое другими словами:

R>Re[11]: паранойя наследования
Автор: rsn81
Дата: 09.07.08

R>Re[13]: паранойя наследования
Автор: rsn81
Дата: 09.07.08


Не совсем. Я говорю о том, что по большому счёту вся разница — в небольшой добавочной косвенности при вызове. На производительность это вряд ли особо влияет.

R>Кстати, не знаю, чего вы так прицепились к Java... это не только там так.


Просто я более-менее хорошо знаю, как там внутрях всё устроено. Так вот вызов "своего" метода от вызова "чужого" метода особо ничем не отличается.
Re[4]: паранойя наследования
От: Plague Россия  
Дата: 09.07.08 09:05
Оценка:
Здравствуйте, igna, Вы писали:

I>Если статическая композиция достаточна, не стоит использовать наследование только для того, чтобы съэкономить на написании forwarding functions.

Тогда я не понял в чем бонус композиции перед наследованием, если:
R>Композиция
R>
  • Плюсы
    R> определяется динамически за счет перекрестных ссылок (не связаны руки компилятором, во время исполнения любой объект может быть подменен другим соответствующего типа);
    динамическую природу вырезали, т.к. она не обязательна...
    R>применяется на основе взаимного соблюдения классами контрактов друг друга, предпочтительно основанных на интерфейсах, то есть не нарушается принцип инкапсуляции;
    При использовании наследования тоже не нарушается принцип инкапсуляции. Доступ к предку может быть ограничен и т.п.
    R>не помню, писали об этом GoF-ы или нет: такие структуры объектов проще понимать, чем сложные графы наследования, ведь композиция приводит к более простым графам наследования, принуждает постепенно писать ориентируясь на шаблоны проектирования, в первую же очередь на основной принцип — одной ответственности класса.
    это очень предвзято... сказать, что композиция хоть как-то упрощает структуру кода.. не думаю...наследование может быть приватным... =)

    Аналогично: не стоит использовать композицию ради того, чтоб избавится от наследования?
    ... << RSDN@Home 1.2.0 alpha rev. 787>>
  • Re[5]: паранойя наследования
    От: igna Россия  
    Дата: 09.07.08 09:13
    Оценка: -1
    Здравствуйте, Plague, Вы писали:

    P>это очень предвзято... сказать, что композиция хоть как-то упрощает структуру кода.. не думаю...наследование может быть приватным... =)


    Против приватного наследования возражать не стану.

    P>Аналогично: не стоит использовать композицию ради того, чтоб избавится от наследования?


    IMHO стоит, если наследование неприватное.
    Re[15]: паранойя наследования
    От: rsn81 Россия http://rsn81.wordpress.com
    Дата: 09.07.08 10:34
    Оценка:
    Здравствуйте, WFrag, Вы писали:

    WF>Легко. Компилировали с одной версией библиотеки, запустили с другой. В Java это сплошь и рядом.

    WF>Это просто пример, что никакой особой "фиксации" не происходит. Вся разница — в отсутствии лишней косвенности и всё.
    WF>Что ню-ню? B-то не перекомпилировали.
    Разговор в теме идет (по-крайней мере начинался) о проектировании в ООП, а не о хаках в конкретных ООЯ. Вы с продуктивом такую петрушку, что описали, устраиваете? Да, было дело, раньше и сам делал подобное даже с обфусцированными условно платными библиотеками. Но ведь нельзя сказать, что это имеет отношение к проектированию мною этих библиотек.

    WF>Не совсем. Я говорю о том, что по большому счёту вся разница — в небольшой добавочной косвенности при вызове. На производительность это вряд ли особо влияет.

    WF>Просто я более-менее хорошо знаю, как там внутрях всё устроено. Так вот вызов "своего" метода от вызова "чужого" метода особо ничем не отличается.
    Говорю про отличия this и this.someReference с точки зрения осведомленности. Вы зря все считаете, что я тут что-то выдумываю, на самом деле тупо цитирую из GoF — книга перед глазами.
    Re[6]: паранойя наследования
    От: Plague Россия  
    Дата: 09.07.08 11:58
    Оценка: -1
    Здравствуйте, igna, Вы писали:

    I>IMHO стоит, если наследование неприватное.


    По моему уже действительно паранойя...

    Сначала, большие возражения против множественного наследования. Да, это очень острый скальпель, и порезаться как нефиг-нафиг. А теперь и просто наследование приселдуется... мне кажется, это происки поклонников структурного программирования! Всемирный заговор! =))

    Наследование более логично, чем композиция, когда есть отношение между объектами "is a". По-сути, при отсутствии реализации наследования его "эмуляли" композицией.
    ... << RSDN@Home 1.2.0 alpha rev. 787>>
    Re[7]: паранойя наследования
    От: Lazy Cjow Rhrr Россия lj://_lcr_
    Дата: 09.07.08 12:35
    Оценка: +3 -1
    Plague,

    I>>IMHO стоит, если наследование неприватное.


    P>По моему уже действительно паранойя...


    P>Сначала, большие возражения против множественного наследования. Да, это очень острый скальпель, и порезаться как нефиг-нафиг. А теперь и просто наследование приселдуется... мне кажется, это происки поклонников структурного программирования! Всемирный заговор! =))


    Наследование классов (в т.ч. и множественное) полностью мажорируется комбой наследование_интерфейсов-реализация (а реализация соответственно — через композицию), так как не подвержена проблеме сильного связывания между классом и подклассом (и "diamond problem" в случае множественного наследования). А если эту комбу навернуть до трейтов-миксинов, то мажорируется в плане удобства программирования тоже.

    P>Наследование более логично, чем композиция, когда есть отношение между объектами "is a". По-сути, при отсутствии реализации наследования его "эмуляли" композицией.


    Сегодня isa, а завтра уже isnota. Или там вставить доп. класс между родителем и потомком (пирожок уже подрумянился, но теперь нужно просверлить дырку и затолкать мясо). Реалии жызни-с...
    ... << RSDN@Home 1.2.0 alpha 4 rev. 1079>>
    quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
    Re[8]: паранойя наследования
    От: Plague Россия  
    Дата: 10.07.08 07:33
    Оценка:
    Здравствуйте, Lazy Cjow Rhrr, Вы писали:

    Если я правильно понял, в своих проектах вы принципиально не испольуете наследования вообще?

    LCR> Наследование классов (в т.ч. и множественное) полностью мажорируется комбой наследование_интерфейсов-реализация (а реализация соответственно — через композицию), так как не подвержена проблеме сильного связывания между классом и подклассом (и "diamond problem" в случае множественного наследования). А если эту комбу навернуть до трейтов-миксинов, то мажорируется в плане удобства программирования тоже.

    Ага, в С тоже нет наследования, соответственно нет и "Diamond problem". Может писать на С?

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

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

    LCR>Сегодня isa, а завтра уже isnota. Или там вставить доп. класс между родителем и потомком (пирожок уже подрумянился, но теперь нужно просверлить дырку и затолкать мясо). Реалии жызни-с...


    А если завтра все в корзину, может сразу ничего не писать?
    Реалии жизни — проблем с наследованиему меня никогда небыло, т.к. оно реализуется на уровне языка. Грамотное наследование — проблема проектирования. Но всегда можно забыть что-то дописать когда пишешь вручную, реалии жызни-с...
    Т.к. наследование выделено на уровне языка в отдельную конструкцию — т.е. не смешано с остальными атрибутами класса, поэтому, я считаю, легче читается.
    ... << RSDN@Home 1.2.0 alpha rev. 787>>
    Re[4]: паранойя наследования
    От: vdimas Россия  
    Дата: 10.07.08 15:58
    Оценка:
    Здравствуйте, rsn81, Вы писали:

    Композиции — это хорошо, и стратегии мне представляются одним из самых удобных и гибких способов композиции, жаль, что язык C# не очень удобен для применения этого паттерна, из-за отсутствия доступа к статическим членам генериков в дизайне и невозможности указать требование для генерика конструктора определённой сигнатуры. Всё решаемо, конечно, буквально одним-двумя лишними методами в контрактах, но это портит красивую картинку самого метода декомпозиции. Правда, и с этим борятся, через аттрибуты, рефлекшен, а вон в новом WPF — и через XML, повышая долю декларативного при использовании этого подхода.

    Интересно, что сами стратегии на практике удобно реализовывать именно как некое дерево наследования, но при этом монстроидальное "общее" дерево наследования системы классов может распасться на малосвязанные небольшие деревца. В самом наследовании реализации никакого криминала нет, скорее — наоборот, просто текущие негативные высказывания относительно наследования реализации существуют из-за потери чувства меры при проектировании системы типов, где, в результате построения ветвистых деревьев наследования, мы получаем не упрощение, а усложнение структуры.
    ... << RSDN@Home 1.2.0 alpha rev. 786>>
    Re[5]: паранойя наследования
    От: rsn81 Россия http://rsn81.wordpress.com
    Дата: 10.07.08 16:42
    Оценка: 4 (1) +1
    Здравствуйте, vdimas, Вы писали:

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

    Честно говоря, не понял суть проблемы и весь героизм ее преодоления.

    V>Интересно, что сами стратегии на практике удобно реализовывать именно как некое дерево наследования, но при этом монстроидальное "общее" дерево наследования системы классов может распасться на малосвязанные небольшие деревца.

    У этих "деревцев" есть вполне себе официальное название — каркасы. И это опять же композиция, за счет повторного использования которой достигается повторное использование не просто кода, а дизайна кода; подробнее описывал здесь: Каркасы и шаблоны проектирования
    Автор(ы): Сергей Рогачев
    Дата: 23.03.2007
    В статье рассматривается вариант реализации шаблона проектирования Model-View-Controller в виде каркаса приложения на основе обобщенного программирования языков Java и C#. В описании предлагаемого решения, кроме того, будут рассмотрены шаблоны проектирования Mediator, Observer и Command и показаны варианты их применения в рассматриваемой реализации Model-View-Controller. Предполагается наличие у читателя знания базовых шаблонов проектирования, языка UML, диаграммами которого будут сопровождаться описания, а также одного из указанных языков программирования.
    .

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

    Никто не утверждал, что наследованием следует полностью прекратить пользоваться, речь шла только о том, что чем более опытным становится проектировщик, тем больше в его коде композиции объектов и тем меньше наследования. А основной негатив относится к применению нубами наследования даже в случае отсутствия между базовым и дочерним классами отношения "является". Выше уже кажется говорили об этом: унаследовавшись от объекта, вместо его агрегирования, разработчик дает любому возможность использования функциональности родителя напрямую, ну со всеми втекающими-вытекающими из этого последствиями.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.