general design advice and code smells - github pages · general design advice and code smells :...

21
General Design Advice and Code Smells Stephen P Levitt School of Electrical and Information Engineering University of the Witwatersrand 2019

Upload: others

Post on 17-Jun-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

General Design Advice and Code Smells

Stephen P Levitt

School of Electrical and Information EngineeringUniversity of the Witwatersrand

2019

Page 2: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Outline

1 Class-Level AdviceStandalone ClassesBase ClassesInterfacesCode Smells (Indicators of Poor Design)

Monolithic ClassData Class

Abstractions at Different Levels

2 Architecture AdviceLayeringProtecting The Domain Layer

General Design Advice and Code Smells 1 / 18

Page 3: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Be Clear About What Kind of Class You Are Writing

The design rules for base classes and standalone classes are very different, and clientcode treats base classes very differently from standalone classes.

Decide what kind of class you need before you design it.

General Design Advice and Code Smells : Class-Level Advice 2 / 18

Page 4: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Standalone Classes

Standalone classes:have a public destructor, copy constructor and assignment operator with valuesemanticshave no virtual functionsare usually instantiated on the stack or as a directly held member of another class(direct composition)are not intended to be used as a base class

General Design Advice and Code Smells : Class-Level Advice : Standalone Classes 3 / 18

Page 5: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Base Classes

Base classes should:establish interfaces which are as simple as possible while still effectively modellinga role in the problem domainhave a destructor which is public and virtualshield code from knowing about the actual type(s) being operated on, by beingused as:

reference parameters of functionsthe type for instantiating smart pointers, or vectors of smart pointers

General Design Advice and Code Smells : Class-Level Advice : Base Classes 4 / 18

Page 6: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Pay Attention to Your Interfaces

“ The most important thing to get right is the interface. Everything elsecan be fixed later. Get the interface wrong and you may never be allowedto fix it.” — Sutter’s Law of Second Chances

“ Interfaces, like diamonds, are forever.”General Design Advice and Code Smells : Class-Level Advice : Interfaces 5 / 18

Page 7: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

class Flight {public:Flight(Aircraft plane,Place orig, Place dest);unsigned int GetMaxSpeed() const;void ScheduleTakeOff(const Time& time);Time GetFlyingTime() const;void AdjustFlightPath(Paths other);

void AddPassenger(const Person& p);void RemovePassenger(const Person& p);vector<Person> GetPassengerList() const;Clearance SecurityCheckPassenger(const Person& p) const;

unsigned int EstimateNoInflightMeals() const;void SetMealTypeAndNumber(MealType meal, unsigned int num);Rands TotalMealCost() const;

};

General Design Advice and Code Smells : Class-Level Advice : Code Smells (Indicators of Poor Design) 6 / 18

Page 8: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Monolithic or Large Class

Catalog of Refactoring — Shvetshttps://refactoring.guru/smells/large-class

General Design Advice and Code Smells : Class-Level Advice : Code Smells (Indicators of Poor Design) 7 / 18

Page 9: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Avoid Monolithic Classes, Prefer Minimal Classes

A minimal class is easier to comprehend and more likely to be used and reused ina variety of situationsA minimal class embodies one concept at the right level of granularity. Amonolithic class is likely to embody several separate concepts and using oneimplies understanding all of the othersMonolithic classes dilute encapsulationMonolithic classes are harder to make correct and error-safe because they tacklemultiple responsibilities

Divide and conquer: small, focused classes are easier to write, get right, testand use.

General Design Advice and Code Smells : Class-Level Advice : Code Smells (Indicators of Poor Design) 8 / 18

Page 10: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Beware the Data Class

Catalog of Refactoring — Shvetshttps://refactoring.guru/smells/data-class

Lots of getter and setter methods but no real behaviour.Consequences:

Clients are forced to define the behaviour ⇒ duplicated codeLittle encapsulation ⇒ if the internal representation of the data class ischanged, clients will have to change

General Design Advice and Code Smells : Class-Level Advice : Code Smells (Indicators of Poor Design) 9 / 18

Page 11: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Abstractions at Different Levels

Classes and their objects are the building blocks of an applicationHigh-level abstractions build upon lower-level abstractions

General Design Advice and Code Smells : Class-Level Advice : Abstractions at Different Levels 10 / 18

Page 12: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Source: Object-Oriented Analysis and Design, 2nd ed., G. Booch, 1994

General Design Advice and Code Smells : Class-Level Advice : Abstractions at Different Levels 11 / 18

Page 13: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Application Layers

PresentationDomainDataLayering — Fowlerhttps://www.martinfowler.com/bliki/PresentationDomainDataLayering.html

General Design Advice and Code Smells : Architecture Advice : Layering 12 / 18

Page 14: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Layer Responsibilities

Presentation Display of information (e.g., in Windows or HTML, handling of userrequests (mouse clicks, keyboard hits), HTTP requests, command-lineinvocations, batch API)

Domain/Logic Logic unique to the systemData Access Communication with databases,other persistent data stores, messaging

systems, transaction managers

General Design Advice and Code Smells : Architecture Advice : Layering 13 / 18

Page 15: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Why Separate Layers?

Understandability, each layer has a coherent set of responsibilities, concerns areseparatedSubstitutability, e.g. easy to substitute different front ends or data storesTestabilitySupports parts of the system changing at different ratesTeam specialisation

General Design Advice and Code Smells : Architecture Advice : Layering 14 / 18

Page 16: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Why Separate Layers?

Understandability, each layer has a coherent set of responsibilities, concerns areseparatedSubstitutability, e.g. easy to substitute different front ends or data storesTestabilitySupports parts of the system changing at different ratesTeam specialisation

General Design Advice and Code Smells : Architecture Advice : Layering 14 / 18

Page 17: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Which Layer’s Code Is The Most Valuable?

The domain layer contains the business IPPresentation and data access layers are typically built from, and use, standardcomponents and frameworks

The domain layer is special ⇒ isolate and protect this layer

General Design Advice and Code Smells : Architecture Advice : Layering 15 / 18

Page 18: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Which Layer’s Code Is The Most Valuable?

The domain layer contains the business IPPresentation and data access layers are typically built from, and use, standardcomponents and frameworks

The domain layer is special ⇒ isolate and protect this layer

General Design Advice and Code Smells : Architecture Advice : Layering 15 / 18

Page 19: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Domain Layer Isolation and Protection

The domain layer shouldmake use of a ubiquitous language (this part of the system should reflect theproblem domain in a very literal way, so the mapping is obvious)contain a clean, expressive domain model unpolluted by infrastructure concernsbe free of (ignorant of)

code for drawing windows, buttons and widgetscode for accessing the database or file systemcode for sending messages (email/SMS)etc.

have limited exposure to external components beyond the team’s control — theserepresent risk if the components change (eg. Web API’s)

General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 16 / 18

Page 20: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Domain Layer Isolation in Practice

“ “You don’t want the domain model to depend on anything that talks toany kind of external system”” — Mathias Verraes

Isolation from the presentation layer happens by default if the dependencies arerightIsolation from the data access layer, and other services, requires you to write yourown classes or interfaces which shield your domain from these externalcomponents

General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18

Page 21: General Design Advice and Code Smells - GitHub Pages · General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 17 / 18. Using Third-Party Libraries

Using Third-Party Libraries Within The Domain Layer

Avoid re-inventing the wheelIt makes sense to use third-party libraries and classes which

are well-written, stable, tested and maintainedare potentially replaceable

Be wary of frameworks and libraries that force you to compromise your designUse library classes as is if they represent genuinely useful abstractions

boost::scoped_ptr was the forerunner of unique_ptrIf necessary, wrap library classes (using composition) to make them meaningful forthe domain, and to expose only the methods that the domain requires

In a “shipping and delivery” domain create a DeliveryDate class which internallyuses the boost date_time library in order to exclude delivery dates on weekends

General Design Advice and Code Smells : Architecture Advice : Protecting The Domain Layer 18 / 18