OO Design with C++: 2. Inheritance, part 2

411 views
361 views

Published on

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
411
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • Public vs. protected interface - two types of clients {part. Meyers1.41}. Containment as "has-a" or "is-implemented-via" relationship {Meyers1.40} Private inheritance - reasons of use instead of containment {Meyers1.42}
  • OO Design with C++: 2. Inheritance, part 2

    1. 1. C++ : наследование, часть 2 Дмитрий Штилерман, Рексофт
    2. 2. Краткое содержание второй части <ul><li>Public & protected interface . </li></ul><ul><li>Случаи видимости наследования ( public, protected, private) и их семантика. </li></ul><ul><li>О тношения “has-a” и “is-implemented-by”. </li></ul><ul><li>Композиция (aggregation). </li></ul><ul><li>Делегирование ( delegation) . </li></ul><ul><li>Изоляция ( insulation ). </li></ul>
    3. 3. Интерфейсы и реализация класса <ul><li>Дизайнер класса B в общем случае проектирует два разных интерфейса: public & protected. </li></ul><ul><li>Public interface - множество всех public members B. Его клиенты - все классы, использующие B. </li></ul><ul><li>Protected interface - множество всех protected members B . Его клиенты - классы, производные от B. </li></ul><ul><li>Как насчет п онятия private interface? Private members класса B являются частью его реализации (implementation), а не интерфейса. </li></ul>
    4. 4. Public & protected interface - пример class Shape { public: virtual void redraw() = 0; protected: void default_redraw(); }; class Tool { public: void applyTo(Shape* pShape) {/*…*/ pShape->redraw();} }; class Rectangle : public Shape { public: virtual void redraw() {/*…*/ default_redraw();} }; <ul><li>Shape:: redraw() - часть public interface . Им могут пользоваться любые классы и функции. </li></ul><ul><li>Shape::default_redraw() - часть protected interface. Им могут пользоваться только производные классы. </li></ul>
    5. 5. Public & protected interface - обозначения <ul><li>I public (B) - class B public interface. </li></ul><ul><li>I protected (B) - class B protected interface. </li></ul><ul><li>I(B) - class B complete interface. I(B)  I public (B)  I protected (B) </li></ul><ul><li>I(B,A) - part of class B interface inherited from its base class A. </li></ul><ul><li>I(B,*) - part of class B interface inherited from all its base classes. </li></ul><ul><li>I(B,B) - part of class B interface declared inside B itself (“own” interface). I x (B)  I x (B,*)  I x (B,B) </li></ul><ul><li>R(B) - class B implementation. </li></ul>
    6. 6. Public inheritance class B { public: void f_public(); protected: void f_protected(); }; class D : public B { public: void g_public(); protected: void g_protected(); }; <ul><li>I public (D) = I public (B)  I public (D,D) </li></ul><ul><li>I protected (D) = I protected (B)  I protected (D,D) </li></ul><ul><li>I(D) = I( B )  I(D,D) </li></ul><ul><li>I public (B) = {B::f_public()} </li></ul><ul><li>I protected (B) = {B::f_protected()} </li></ul><ul><li>I public (D) = {B::f_public(), D::g_public()} </li></ul><ul><li>I protected (D) = {B::f_protected(), D::g_protected()} </li></ul>Public и protected интерфейсы B добавляются в соответствующие интерфейсы D.
    7. 7. Protected inheritance class B { public: void f_public(); protected: void f_protected(); }; class D : protected B { public: void g_public(); protected: void g_protected(); }; <ul><li>I public (B) = {B::f_public()} </li></ul><ul><li>I protected (B) = {B::f_protected()} </li></ul><ul><li>I public (D) = {D::g_public()} </li></ul><ul><li>I protected (D) = { B::f_public(), B::f_protected(), D::g_protected()} </li></ul><ul><li>I public (D) = I public (D,D) </li></ul><ul><li>I protected (D) = I(B)  I protected (D,D) </li></ul><ul><li>I(D) = I( B )  I(D,D) </li></ul>Public и protected интерфейсы B добавляются в protected интерфейс D.
    8. 8. Private inheritance class B { public: void f_public(); protected: void f_protected(); }; class D : private B { public: void g_public(); protected: void g_protected(); }; <ul><li>I public (B) = {B::f_public()} </li></ul><ul><li>I protected (B) = {B::f_protected()} </li></ul><ul><li>I public (D) = {D::g_public()} </li></ul><ul><li>I protected (D) = {D::g_protected()} </li></ul><ul><li>I public (D) = I public (D,D) </li></ul><ul><li>I protected (D) = I protected (D,D) </li></ul><ul><li>I(D,*) = I( D,B ) =  </li></ul><ul><li>I(D) = I(D,D) </li></ul><ul><li>R(D)  I(B) (!!!) </li></ul>Public и protected интерфейсы B становятся частью реализации D.
    9. 9. Когда какое наследование возможно использовать? <ul><li>Public : когда хотим интерфейсы базового класса раздать всем своим клиентам. I(Derived)  I(Base). </li></ul><ul><li>Protected : когда хотим интерфейсы базового класса раздать своим производным классам (клиентам protected интерфейса). I protected (Derived)  I(Base). </li></ul><ul><li>Private : когда хотим воспользоваться интерфейсами базового класса сами, т.е. включить их в нашу реализацию. R(Derived)  I(Base). </li></ul>
    10. 10. Почему обычно не нужно использовать наследование, отличное от public? <ul><li>Любое наследование соответствует отношению “is-a”. </li></ul><ul><li>Отношение “is-a” концептуально (на уровне анализа) является частью интерфейса класса и определяет тип объекта. </li></ul><ul><li>Изменяя видимость наследования мы меняем видимость отношения “is-a” для клиентов класса . </li></ul><ul><li>На практике очень редко встречается ситуация, когда видимость отношения “is-a” различна для разных клиентов. </li></ul>
    11. 11. Отношение “has-a” - к омпозиция (aggregation) class Caption {…}; class Label {…}; class Button {…}; class MessageBox { Caption m_caption; Label m_label; Button m_buttonOK; Button m_buttonCancel; }; <ul><li>Отношение “has-a” на практике всегда реализуется через композицию ( aggregation) . See also: Composite pattern. </li></ul><ul><li>Отношение “has-a” концептуально отлично от отношения “is-a”. </li></ul><ul><li>NB: не стоит реализовывать отношение “has-a” через public data members! Лучше делать это через accessor methods , внимательно обдумывая интерфейс. </li></ul>
    12. 12. Отношени е “is-implemented-by” - делегирование (delegation) <ul><li>Концептуально отношение “is-implemented-by” всегда является частью реализации. </li></ul><ul><li>Толкования слова “делегирование” из словаря: “передача прав и ответственности подчиненному”, “перед а ча полномочий” . </li></ul><ul><li>Делегирование - частный случай использования. </li></ul><ul><li>Что происходит с интерфейсами? </li></ul><ul><ul><li>В случае делегирования на уровне public или protected методов мы делаем так, что I(A)  I public (B), но при этом B не является базовым классом A. </li></ul></ul><ul><ul><li>В случае делегирования на уровне private методов делегирование является частью реализации. </li></ul></ul>class A { B m_b; void f() {m_b.f();} void g() {m_b.g();} }; class B { public: void f() {…} void g() {…} };
    13. 13. И золяция ( insulation ) <ul><li>Изоляция ( insulation) - разновидность инкапсуляции, когда клиенты класса полностью изолированы от деталей реализации. </li></ul><ul><li>Изоляция - разновидность композиции (с точки зрения структуры). </li></ul><ul><li>Синонимы: метафора “handle-body”, метафора “letter-envelope”. </li></ul><ul><li>John Lakos, “Large-Scale C+ Software Design” </li></ul>// Файл gadget.h class GadgetImpl; class Gadget { private: GadgetImpl* m_pImpl; public: Gadget(); void f(); }; // Файл gadget.cpp #include “gadget.h” class GadgetImpl { public: void f() {…} }; Gadget::Gadget() {m_pImpl = new GadgetImpl;} void Gadget::f() {m_pImpl->f();}
    14. 14. P rivate inheritance как инкапсуляция базового класса <ul><li>Хотим использовать protected интерфейс класса B  в ынуждены от него наследовать. </li></ul><ul><li>Отношение “is-implemented-by” есть часть реализации  наследование д.б. private . </li></ul><ul><li>Вопрос: Зачем такое извращение м.б. нужно? Ответ: Когда мы хотим сделать частью реализации сам базовый класс! </li></ul>class A : private B { void f() {B::f();} void g() {B::g();} }; class B { public: void f() {…} protected: void g() {…} };
    15. 15. P rivate inheritance как упаковка общей части реализации <ul><li>Хотим абстрагировать для нескольких наших классов их общую реализацию, выделив ее в отдельный класс. </li></ul><ul><li>Не хотим, чтобы ей пользовался кто-то другой. </li></ul><ul><li>По каким-то причинам не хотим или не можем использовать изоляцию. </li></ul><ul><li>Часто встречающаяся уважительная причина - реализация шаблонов . </li></ul>class GadgetBase { protected: GadgetBase(); void f() {/* Очень умный алгоритм */} }; template<class T> class Gadget : private GadgetBase { public: void f() {GadgetBase::f();} };
    16. 16. Подведение итогов <ul><li>Основной вопрос философии </li></ul><ul><li>Что хотел сказать автор своим дизайном? </li></ul><ul><li>Правила правой руки </li></ul><ul><li>Отношение “is-a”: public inheritance </li></ul><ul><li>Отношение “has-a”: aggregation </li></ul><ul><li>Отношение “is-implemented-by”: </li></ul><ul><ul><li>Изоляция не нужна или невозможна: aggregation </li></ul></ul><ul><ul><li>Изоляция нужна и возможна: insulation </li></ul></ul><ul><ul><li>Желательна упаковка общей реализации нескольких классов: private inheritance </li></ul></ul>
    17. 17. Продолжение в следующей серии <ul><li>Множественное наследование . </li></ul><ul><li>Какое оно бывает? </li></ul><ul><li>Что оно значит? </li></ul><ul><li>Как с ним бороться? </li></ul>

    ×