solid deconstruction

99
SOLID Deconstruction @KevlinHenney

Upload: kevlin-henney

Post on 13-Jan-2017

295 views

Category:

Software


0 download

TRANSCRIPT

Page 1: SOLID Deconstruction

SOLID Deconstruction @KevlinHenney

Page 2: SOLID Deconstruction
Page 3: SOLID Deconstruction
Page 4: SOLID Deconstruction

S

O

L

I

D

Page 5: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 6: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 7: SOLID Deconstruction

In object-oriented programming, the single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.

http://en.wikipedia.org/wiki/Single_responsibility_principle

Page 8: SOLID Deconstruction

The term was introduced by Robert C. Martin [...]. Martin described it as being based on the principle of cohesion, as described by Tom DeMarco in his book Structured Analysis and Systems Specification.

http://en.wikipedia.org/wiki/Single_responsibility_principle

Page 9: SOLID Deconstruction
Page 10: SOLID Deconstruction
Page 11: SOLID Deconstruction
Page 12: SOLID Deconstruction
Page 13: SOLID Deconstruction

We refer to a sound line of reasoning,

for example, as coherent. The thoughts

fit, they go together, they relate to each

other. This is exactly the characteristic

of a class that makes it coherent: the

pieces all seem to be related, they seem

to belong together, and it would feel

somewhat unnatural to pull them apart.

Such a class exhibits cohesion.

Page 14: SOLID Deconstruction

utility

the state of being useful, profitable or beneficial useful, especially through having several functions functional rather than attractive

Concise Oxford English Dictionary

Page 15: SOLID Deconstruction

#include <stdlib.h>

Page 16: SOLID Deconstruction

Every class should

embody only about 3–5

distinct responsibilities.

Grady Booch, Object Solutions

Page 17: SOLID Deconstruction
Page 18: SOLID Deconstruction

One of the most foundational principles of good design is:

Gather together those things that change for the same reason, and separate those things that change for different reasons.

This principle is often known as the single responsibility principle, or SRP. In short, it says that a subsystem, module, class, or even a function, should not have more than one reason to change.

Page 19: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 20: SOLID Deconstruction

Interface inheritance (subtyping) is used whenever one can imagine that client code should depend on less functionality than the full interface. Services are often partitioned into several unrelated interfaces when it is possible to partition the clients into different roles. For example, an administrative interface is often unrelated and distinct in the type system from the interface used by “normal” clients.

"General Design Principles" CORBAservices

Page 21: SOLID Deconstruction

The dependency should be on the interface, the whole interface, and nothing but the interface.

Page 22: SOLID Deconstruction
Page 23: SOLID Deconstruction

We refer to a sound line of reasoning,

for example, as coherent. The thoughts

fit, they go together, they relate to each

other. This is exactly the characteristic

of a class that makes it coherent: the

pieces all seem to be related, they seem

to belong together, and it would feel

somewhat unnatural to pull them apart.

Such a class exhibits cohesion.

Page 24: SOLID Deconstruction

We refer to a sound line of reasoning,

for example, as coherent. The thoughts

fit, they go together, they relate to each

other. This is exactly the characteristic of

an interface that makes it coherent: the

pieces all seem to be related, they seem

to belong together, and it would feel

somewhat unnatural to pull them apart.

Such an interface exhibits cohesion.

Page 25: SOLID Deconstruction

public interface LineIO { String read(); void write(String toWrite); }

Page 26: SOLID Deconstruction

public interface LineReader { String read(); } public interface LineWriter { void write(String toWrite); }

Page 27: SOLID Deconstruction
Page 28: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 29: SOLID Deconstruction
Page 30: SOLID Deconstruction

In a purist view of object-oriented methodology,

dynamic dispatch is the only mechanism for

taking advantage of attributes that have been

forgotten by subsumption.

This position is often taken on abstraction

grounds: no knowledge should be obtainable

about objects except by invoking their methods.

In the purist approach, subsumption provides a

simple and effective mechanism for hiding

private attributes.

Page 31: SOLID Deconstruction

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

Barbara Liskov "Data Abstraction and Hierarchy"

Page 32: SOLID Deconstruction

generalisation

specialisation

commonality

variation

Page 33: SOLID Deconstruction

E.g., Python's dict class, which is a hashed mapping container

E.g., Python's OrderedDict class, which preserves order of insertion

Page 34: SOLID Deconstruction
Page 35: SOLID Deconstruction
Page 36: SOLID Deconstruction

public class RecentlyUsedList { ...

public int Count { get ... } public string this[int index] { get ... } public void Add(string newItem) ... ... }

Page 37: SOLID Deconstruction

public class RecentlyUsedList { private IList<string> items = new List<string>();

public int Count { get { return items.Count; } } public string this[int index] { get { return items[index]; } } public void Add(string newItem) { if(newItem == null) throw new ArgumentNullException(); items.Remove(newItem); items.Insert(0, newItem); } ... }

Page 38: SOLID Deconstruction

public class RecentlyUsedList : List<string> { public override void Add(string newItem) { if(newItem == null) throw new ArgumentNullException(); items.Remove(newItem); items.Insert(0, newItem); } ... }

Page 39: SOLID Deconstruction

namespace List_spec { ... [TestFixture] public class Addition { private List<string> list; [Setup] public void List_is_initially_empty() { list = ... } ... [Test] public void Addition_of_non_null_item_is_appended() ... [Test] public void Addition_of_null_is_permitted() ... [Test] public void Addition_of_duplicate_item_is_appended() ... ... } ... }

Page 40: SOLID Deconstruction

namespace List_spec { ... [TestFixture] public class Addition { private List<string> list; [Setup] public void List_is_initially_empty() { list = new List<string>(); } ... [Test] public void Addition_of_non_null_item_is_appended() ... [Test] public void Addition_of_null_is_permitted() ... [Test] public void Addition_of_duplicate_item_is_appended() ... ... } ... }

Page 41: SOLID Deconstruction

namespace List_spec { ... [TestFixture] public class Addition { private List<string> list; [Setup] public void List_is_initially_empty() { list = new RecentlyUsedList(); } ... [Test] public void Addition_of_non_null_item_is_appended() ... [Test] public void Addition_of_null_is_permitted() ... [Test] public void Addition_of_duplicate_item_is_appended() ... ... } ... }

Page 42: SOLID Deconstruction

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

Barbara Liskov "Data Abstraction and Hierarchy"

Page 43: SOLID Deconstruction

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

Barbara Liskov "Data Abstraction and Hierarchy"

Page 44: SOLID Deconstruction

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

Barbara Liskov "Data Abstraction and Hierarchy"

Page 45: SOLID Deconstruction

A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.

Barbara Liskov "Data Abstraction and Hierarchy"

Page 46: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 47: SOLID Deconstruction

Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:

Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Robert C Martin "The Open-Closed Principle"

C++ Report, January 1996

Page 48: SOLID Deconstruction

Open for extension

Closed for extension

Closed for modification

Open for modification

Extend existing code without changing it, e.g., using a framework

Extend or change existing code, e.g., open-source and unpublished code

Non-extensible and unchangeable code, e.g., composable code or legacy code

Non-extensible code that can be maintained, e.g., composable code in a maintained library

Page 49: SOLID Deconstruction
Page 50: SOLID Deconstruction

The principle stated that a good module

structure should be both open and closed:

Closed, because clients need the

module's services to proceed with their

own development, and once they have

settled on a version of the module should

not be affected by the introduction of new

services they do not need.

Open, because there is no guarantee that

we will include right from the start every

service potentially useful to some client.

Page 51: SOLID Deconstruction

[...] A good module structure

should be [...] closed [...] because

clients need the module's

services to proceed with their

own development, and once

they have settled on a version of

the module should not be

affected by the introduction of

new services they do not need.

Page 52: SOLID Deconstruction

Published Interface is a term I used (first in Refactoring) to refer to a class interface that's used outside the code base that it's defined in.

Martin Fowler http://martinfowler.com/bliki/PublishedInterface.html

Page 53: SOLID Deconstruction
Page 54: SOLID Deconstruction

There is no problem changing a

method name if you have access to

all the code that calls that method.

Even if the method is public, as long

as you can reach and change all the

callers, you can rename the method.

Page 55: SOLID Deconstruction

There is a problem only if the

interface is being used by code that

you cannot find and change. When

this happens, I say that the interface

becomes a published interface (a

step beyond a public interface).

Page 56: SOLID Deconstruction

Published Interface is a term I used (first in Refactoring) to refer to a class interface that's used outside the code base that it's defined in.

The distinction between published and public is actually more important than that between public and private.

The reason is that with a non-published interface you can change it and update the calling code since it is all within a single code base. [...] But anything published so you can't reach the calling code needs more complicated treatment.

Martin Fowler http://martinfowler.com/bliki/PublishedInterface.html

Page 57: SOLID Deconstruction

[...] A good module structure

should be [...] open [...] because

there is no guarantee that we will

include right from the start every

service potentially useful to some

client.

Page 58: SOLID Deconstruction
Page 59: SOLID Deconstruction

Speculative Generality

Brian Foote suggested this name for a

smell to which we are very sensitive.

You get it when people say, "Oh, I

think we need the ability to do this

kind of thing someday" and thus want

all sorts of hooks and special cases to

handle things that aren't required.

Page 60: SOLID Deconstruction

extends

Page 61: SOLID Deconstruction

A myth in the object-oriented design

community goes something like this:

If you use object-oriented technology,

you can take any class someone else

wrote, and, by using it as a base class,

refine it to do a similar task.

Robert B Murray

C++ Strategies and Tactics

Page 62: SOLID Deconstruction
Page 63: SOLID Deconstruction

Design and implement for

inheritance or else prohibit it

By now, it should be apparent that

designing a class for inheritance places

substantial limitations on the class.

Page 64: SOLID Deconstruction
Page 65: SOLID Deconstruction

http://blog.8thlight.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html

Page 66: SOLID Deconstruction

http://blog.8thlight.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html

I've heard it said that the OCP is wrong, unworkable, impractical, and not for real programmers with real work to do. The rise of plugin architectures makes it plain that these views are utter nonsense. On the contrary, a strong plugin architecture is likely to be the most important aspect of future software systems.

Page 67: SOLID Deconstruction

OCP ≡ Plug-ins?

Page 68: SOLID Deconstruction

OCP ≡ Plug-ins /

Page 69: SOLID Deconstruction

public abstract class Shape

{

...

}

public class Square : Shape

{

...

public void DrawSquare() ...

}

public class Circle : Shape

{

...

public void DrawCircle() ...

}

Page 70: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

if (s is Square)

(s as Square).DrawSquare();

else if (s is Circle)

(s as Circle).DrawCircle();

}

Page 71: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

if (s is Square)

(s as Square).DrawSquare();

else if (s is Circle)

(s as Circle).DrawCircle();

}

Page 72: SOLID Deconstruction

public abstract class Shape

{

...

public abstract void Draw();

}

public class Square : Shape

{

...

public override void Draw() ...

}

public class Circle : Shape

{

...

public override void Draw() ...

}

Page 73: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 74: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 75: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 76: SOLID Deconstruction

Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:

Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Robert C Martin "The Open-Closed Principle"

C++ Report, January 1996

Page 77: SOLID Deconstruction

Bertrand Meyer gave us guidance as long ago as 1988 when he coined the now famous open-closed principle. To paraphrase him:

Software entites (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Robert C Martin "The Open-Closed Principle"

C++ Report, January 1996

Page 78: SOLID Deconstruction

This double requirement looks like a

dilemma, and classical module

structures offer no clue.

This double requirement looks like a

dilemma, and classical module

structures offer no clue. But

inheritance solves it.

This double requirement looks like a

dilemma, and classical module

structures offer no clue. But

inheritance solves it. A class is

closed, since it may be compiled,

stored in a library, baselined, and

used by client classes. But it is also

open, since any new class may use

it as parent, adding new features.

Page 79: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

if (s is Square)

(s as Square).DrawSquare();

else if (s is Circle)

(s as Circle).DrawCircle();

}

Page 80: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

if (s is Square)

(s as Square).DrawSquare();

else if (s is Circle)

(s as Circle).DrawCircle();

}

Page 81: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 82: SOLID Deconstruction

public abstract class Shape ...

public class Square : Shape ...

public class Circle : Shape ...

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 83: SOLID Deconstruction

public sealed class Shape

{

public enum Type { Square, Circle }

...

public void Draw() ...

}

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 84: SOLID Deconstruction

public sealed class Shape

{

public enum Type { Square, Circle }

...

public void Draw() ...

}

static void DrawAllShapes(Shape[] list)

{

foreach (Shape s in list)

s.Draw();

}

Page 85: SOLID Deconstruction

Don't publish

interfaces prematurely.

Modify your code

ownership policies to

smooth refactoring.

Page 86: SOLID Deconstruction

Single Responsibility

Open-Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 87: SOLID Deconstruction

In object-oriented programming, the dependency inversion principle refers to a specific form of decoupling where conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are inverted (i.e. reversed) for the purpose of rendering high-level modules independent of the low-level module implementation details.

http://en.wikipedia.org/wiki/Dependency_inversion_principle

Page 88: SOLID Deconstruction

The principle states:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions.

http://en.wikipedia.org/wiki/Dependency_inversion_principle

Page 89: SOLID Deconstruction

inversion, noun

the action of inverting or the state of being

inverted

reversal of the normal order of words,

normally for rhetorical effect

an inverted interval, chord, or phrase

a reversal of the normal decrease of air

temperature with altitude, or of water

temperature with depth

Concise Oxford English Dictionary

Page 90: SOLID Deconstruction

Parameterize

from Above

Hardwire

from Below

Page 91: SOLID Deconstruction
Page 92: SOLID Deconstruction
Page 93: SOLID Deconstruction
Page 94: SOLID Deconstruction
Page 95: SOLID Deconstruction

Stewart Brand, How Buildings Learn See also http://www.laputan.org/mud/

Page 96: SOLID Deconstruction

package com.sun...;

Page 97: SOLID Deconstruction

Scenario buffering by dot-voting possible changes and invert dependencies as needed

Page 98: SOLID Deconstruction

One of the most foundational principles of good design is:

Gather together those things that change for the same reason, and separate those things that change for different reasons.

This principle is often known as the single responsibility principle, or SRP. In short, it says that a subsystem, module, class, or even a function, should not have more than one reason to change.

Page 99: SOLID Deconstruction

At some level

the style

becomes the

substance.