OO Design with C++: 4. Design Principles, part 1

672 views

Published on

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

No Downloads
Views
Total views
672
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

OO Design with C++: 4. Design Principles, part 1

  1. 1. C++ : принципы проектирования, часть 1 Дмитрий Штилерман, Рексофт
  2. 2. Что такое «хорошо» и что такое «плохо» ? <ul><li>Что такое «плохой» дизайн? Признаки «плохого» дизайна: </li></ul><ul><ul><li>« Жесткость » ( rigidity ): трудно менять, т.к. изменение в одном месте влечет за собой необходимость изм енений во многих других местах. </li></ul></ul><ul><ul><li>«Хрупкость» ( fragility ): трудно поддерживать, т.к. исправление бага в одном месте создает несколько багов в других местах. </li></ul></ul><ul><ul><li>«Неподъемность» ( immobility ): трудно повторно использовать, т.к. каждый компонент зависит от всех остальных. </li></ul></ul><ul><li>Что такое «хороший» дизайн? </li></ul><ul><ul><li>Если дизайн не обладает признаками «плохого», то он не так уж плох. </li></ul></ul>
  3. 3. Что на свете самое жесткое, хрупкое и неподъемное? // My TreeView.cpp #include “MyTreeView.h” #include “MyMainWindow.h” #include “MyListView.h” #include “MyDetailsView.h” #include “MyFileSystem.h” void CMyTreeView::OnSelChanged() { ITEM nCurItem = GetCurSel(); m_pMainWnd->m_pListView ->SetCurSel(nCurItem); m_pMainWnd->m_pDetailsView ->LoadItemInfo(nCurItem); } Ответ: макароны! // MyFileSystem.cpp #include “MyFileSystem.h” #include “MyTreeView.h” #include “MyMainWindow.h” #include “MyListView.h” #include “MyDetailsView.h” void MyFileSystem::OnFileChange( const char* pszFileName) { ITEM nCurItem = GetFileItemID( pszFileName); m_pMainWnd->m_pTreeView ->SetCurSel(nCurItem); m_pMainWnd->m_pListView ->SetCurSel(nCurItem); m_pMainWnd->m_pDetailsView ->LoadItemInfo(nCurItem); }
  4. 4. Зависимости между компонентами системы <ul><li>Компонент A зависит от компонента B: A  B. </li></ul><ul><li>Логическая зависимость (logical dependency) - зависимость между логическими компонентами системы (class, function). </li></ul><ul><ul><li>A использует B в своем интерфейсе: I(A)  B . </li></ul></ul><ul><ul><li>A использует B в своей реализации: R(A)  B. </li></ul></ul><ul><li>Физическая зависимость (physical dependency) - зависимость между физическими компонентами системы (module, namespace, package). </li></ul><ul><ul><li>Некий л огический компонент, упакованный в физический компонент X, зависит от некоего логического компонента, упакованного в физический компонент Y: X  Y . </li></ul></ul>
  5. 5. Графы и уровни зависимостей <ul><li>Ациклический орграф ( DAG ) </li></ul><ul><li>Независимые компоненты - уровень 1. </li></ul><ul><li>Компоненты уровня N зависят от компонентов уровня не выше N-1. </li></ul><ul><li>Циклический орграф </li></ul><ul><li>Невозможно присвоить номера уровней. </li></ul>
  6. 6. Примеры графов зависимостей <ul><li>Если зависимостей слишком много, это означает «макароны» и все признаки плохого дизайна. </li></ul><ul><li>Если зависимостей слишком мало, это может означать недостаточное повторное использование и копирование кода . </li></ul><ul><li>Более рискованно иметь слишком много, чем слишком мало зависимостей. </li></ul>
  7. 7. Метрика зависимости для компонента <ul><li>Component Dependency (CD) - оценивает степень зависимости индивидуального компонента: </li></ul><ul><ul><li>CD компонента, не зависящего ни от кого, равна 1. </li></ul></ul><ul><ul><li>CD компонента, зависящего от других компонентов, равна сумме CD этих компонентов плюс 1. </li></ul></ul><ul><li>Объяснение семантики CD: </li></ul><ul><ul><li>Количество компонентов, которые необходимы для тестирования данного компонента. </li></ul></ul><ul><ul><li>Приблизительный смысл: стоимость компиляции и компоновки. </li></ul></ul><ul><li>В случае циклических зависимостей CD может не иметь смысла и компонент может зависеть от всей системы (нелокальные зависимости) . </li></ul>
  8. 8. Метрики зависимости для системы <ul><li>Cumulative Component Dependency (CCD): сумма CD всех компонентов системы. </li></ul><ul><li>Average CD (ACD): с редняя CD по всем компонентам системы ( CCD деленная на число компонентов). </li></ul><ul><li>Normalized CCD (NCCD) : ACD оцениваемой системы разделенная на ACD системы с графом зависимостей в форме сбалансированного бинарного дерева (Balanced Binary Tree, BBT) . </li></ul>
  9. 9. Примеры метрик зависимости для разных систем <ul><li>N - число компонентов системы. </li></ul><ul><li>Для BBT : CCD=O(N·log(N)); ACD=O(log(N)); NCCD  1 </li></ul><ul><li>Для полного графа (все зависят от всех) : CCD=N 2 ; ACD=N; NCCD=O(N/log(N)) </li></ul><ul><li>Для «вырожденной системы» (все независимы): CCD=N; ACD=1; NCCD=O(1/log(N)) </li></ul><ul><li>Для «плоской системы» (один компонент зависит от N-1 независимых) : CCD=2·N-1=O(N); ACD=O(1); NCCD=O(1/log(N)) </li></ul><ul><li>Для «строго вертикальной системы» (по одному компоненту на уровень): CCD=(N·(N+1))/2=O(N 2 ); ACD=O(N); NCCD=O(N/log(N)) </li></ul>
  10. 10. Интерпретация метрик зависимости <ul><li>«Вертикальные» ациклические и циклические структуры </li></ul><ul><ul><li>От BBT до полного графа. </li></ul></ul><ul><ul><li>CCD=O(N 2 ); ACD=O(N); NCCD=O(N/log(N))>1 </li></ul></ul><ul><ul><li>В пределе - слишком много зависимостей. </li></ul></ul><ul><li>«Горизонтальные» ациклические структуры </li></ul><ul><ul><li>От BBT до « вырожденной системы » . </li></ul></ul><ul><ul><li>CCD=O(N); ACD=O(1); NCCD=O(1/log(N))<1 </li></ul></ul><ul><ul><li>В пределе - слишком мало зависимостей </li></ul></ul>
  11. 11. Оптимизация структуры зависимостей <ul><li>Общая задача дизайнера - минимизировать зависимости в системе ( CCD  min ). </li></ul><ul><li>Кроме того, существует ряд оптимизирующих преобразований, сохраняющих функциональность системы. </li></ul><ul><ul><li>Insulation ( изоляция ): зависимость интерфейса преобразуется в зависимость реализации (см. прошлую лекцию). </li></ul></ul><ul><ul><li>Levelization ( ранжирование ): устранение циклических зависимостей путем разбиения компонентов на новые компоненты (обычно находящиеся на разных уровнях зависимости). </li></ul></ul>
  12. 12. Общий случай ранжирования для двух компонентов
  13. 13. Escalation (ранжирование «подъемом») class B { A ConvertToA(); }; class A { B ConvertToB(); }; class B { }; class A { }; namespace ABConverter { B ConvertAToB(A); A ConvertBToA(B); };
  14. 14. Demotion (ранжирование «спуском») class B { enum EB {…}; void g(EA, EB); }; class A { enum EA {…}; void f(EA, EB); }; class B { void g(EA, EB); }; class A { void f(EA, EB); }; namespace E { enum EA {…}; enum EB {…}; };
  15. 15. Другие принципы и способы использования ранжирования <ul><li>Способы: </li></ul><ul><ul><li>Разбиение монолитного компонента на разноуровневые подкомпоненты. </li></ul></ul><ul><ul><li>Разбиение абстрактного класса на протокол и частичную реализацию. </li></ul></ul><ul><li>Принципы: </li></ul><ul><ul><li>“ Escalate policy and demote infrastructure”. </li></ul></ul><ul><ul><li>Разбиение ведет к увеличению как гибкости, так и сложности системы. John Lakos, “Large-Scale C+ Software Design” </li></ul></ul>

×