Reader small image

You're reading from  TypeScript Design Patterns

Product typeBook
Published inAug 2016
Reading LevelIntermediate
PublisherPackt
ISBN-139781785280832
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Vilic Vane
Vilic Vane
author image
Vilic Vane

Vilic Vane is a JavaScript engineer with over 8 years of experience in web development. He started following the TypeScript project since it went public, and hes also a contributor of the project. He is now working at Ruff, a startup company building an IoT platform that runs JavaScript on embedded devices.
Read more about Vilic Vane

Right arrow

Chapter 8. SOLID Principles

SOLID Principles are well-known Object-Oriented Design (OOD)principles summarized by Uncle Bob (Robert C. Martin). The word SOLID comes from the initials of the five principles it refers to, including Single responsibility principle, Open-closed principle, Liskov substitution principle, Interface segregation principle and Dependency inversion principle. Those principles are closely related to each other, and can be a great guidance in practice.

Here is a widely used summary of SOLID principles from Uncle Bob:

  • Single responsibility principle: A class should have one, and only one, reason to change

  • Open-closed principle: You should be able to extend a classes behavior, without modifying it

  • Liskov substitution principle: Derived classes must be substitutable for their base classes

  • Interface segregation principle: Make fine-grained interfaces that are client specific

  • Dependency inversion principle: Depend on abstractions, not on concretions

In this chapter, we will...

Single responsibility principle


The single responsibility principle declares that a class should have one, and only one reason to change. And the definition of the world reason in this sentence is important.

Example

Consider a Command class that is designed to work with both command-line interface and graphical user interface:

class Command { 
  environment: Environment; 
 
  print(items: ListItem[]) { 
    let stdout = this.environment.stdout; 
   
    stdout.write('Items:\n'); 
   
    for (let item of items) { 
      stdout.write(item.text + '\n'); 
    } 
  } 
   
  render(items: ListItem[]) { 
    let element = <List items={items}></List>; 
    this.environment.render(element); 
  } 
   
  execute() { } 
} 

To make this actually work, execute method would need to handle both the command execution and result displaying:

class Command { 
  .. 
   
  execute() {
 ...

Open-closed principle


The open-closed principle declares that you should be able to extend a class' behavior, without modifying it. This principle is raised by Bertrand Meyer in 1988:

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

A program depends on all the entities it uses, that means changing the already-being-used part of those entities may just crash the entire program. So the idea of the open-closed principle is straightforward: we'd better have entities that never change in any way other than extending itself.

That means once a test is written and passing, ideally, it should never be changed for newly added features (and it needs to keep passing, of course). Again, ideally.

Example

Consider an API hub that handles HTTP requests to and responses from the server. We are going to have several files written as modules, including http-client.ts, hub.ts and app.ts (but we won't actually write http-client.ts in this example...

Liskov substitution principle


The open-closed principle is the essential principle of keeping code maintainable and reusable. And the key to the open-closed principle is abstraction with polymorphism. Behaviors like implementing interfaces, or extending classes make polymorphic shapes, but that might not be enough.

The Liskov substitution principle declares that derived classes must be substitutable for their base classes. Or in the words of Barbara Liskov, who raised this principle:

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.

Never mind. Let's try another one: any foreseeable usage of the instance of a class should be working with the instances of its derived classes.

Example

And here we go with a straightforward violation example. Consider Noodles and InstantNoodles...

Interface segregation principle


We've already discussed the important role played by abstractions in object-oriented design. The abstractions and their derived classes without separation usually come up with hierarchical tree structures. That means when you choose to create a branch, you create a parallel abstraction to all of those on another branch.

For a family of classes with only one level of inheritance, this is not a problem: because it is just what you want to have those classes derived from. But for a hierarchy with greater depth, it could be.

Example

Consider the TextReader example we took with Template Method Pattern in Chapter 6, Behavioral Design Patterns: Continuous we had FileAsciiTextReader and HttpAsciiTextReader derived from AsciiTextReader. But what if we want to have other readers that understand UTF-8 encoding?

To achieve that goal, we have two common options: separate the interface into two for different objects that cooperate, or separate the interface into two then get...

Dependency inversion principle


When we talk about dependencies, the natural sense is about dependencies from bottom to top, just like how buildings are built. But unlike a building that stands for tens of years with little change, software keeps changing during its life cycle. Every change costs, more or less.

The dependency inversion principle declares that entities should depend on abstractions, not on concretions. Higher level code should not depend directly on low-level implementations, instead, it should depend on abstractions that lead to those implementations. And this is why things are inverse.

Example

Still taking the HTTP client and API hub as an example, which obviously violates the dependency inversion principle, taking the foreseeable application into consideration, what the API hub should depend on is a messaging mechanism bridging client and server, but not bare HTTP client. This means we should have an abstraction layer of messaging before the concrete implementation of HTTP...

Summary


In this chapter, we walked through the well-known SOLID principles with simple examples. Sometimes, following those principles could lead us to a useful design pattern. And we also found that those principles are strongly bound to each other. Usually violating one of them may indicate other violations.

Those principles could be extremely helpful for OOD, but could also be overkill if they are applied without proper adaptions. A well-designed system should have those principles confirmed just right, or it might harm.

In the next chapter, instead of theories, we'll have more time with a complete workflow with testing and continuous integration involved.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
TypeScript Design Patterns
Published in: Aug 2016Publisher: PacktISBN-13: 9781785280832
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Vilic Vane

Vilic Vane is a JavaScript engineer with over 8 years of experience in web development. He started following the TypeScript project since it went public, and hes also a contributor of the project. He is now working at Ruff, a startup company building an IoT platform that runs JavaScript on embedded devices.
Read more about Vilic Vane