zl程序教程

您现在的位置是:首页 >  其它

当前栏目

SOLID -- OOP design principles

-- Design OOP solid
2023-09-27 14:28:36 时间

SOLID

https://team-coder.com/solid-principles/

OOP五原则,帮助开发者设计 可维护 和 可扩展的类。

 

SOLID is an acronym for five principles that help software developers design maintainable and extendable classes. It stands for Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion. The acronym was first introduced by Michael Feathers and is based on Uncle Bob’s paper Design Principles and Design Patterns.

This article is a summary of the SOLID principles as originally introduced by Uncle Bob. I explain each of the five principles with an example.

 

Single Responsibility Principle

类, 模块, 包 职责 明确。

不搞万能军刀。

Single Responsibility Principle

A class should have one, and only one, reason to change.

Every class is responsible for exactly one thing. That means it has only one reason to change. If you change anything in that class, it will effect only one particular behavior of the software. That makes the code more robust because there will be less side effects. If a class had two responsibilities and you changed anything, the risk would be high that you also break the logic for the second behavior.

Example

A ScoreCounter class is only responsible for counting the score of a game according to the scoring rules. This class should only change if the scoring rules change.

 

Open-Closed Principle

模块, 对修改封闭, 对扩展开放。

 

Open-Closed Principle

You should be able to extend a classes behavior, without modifying it.

The classes you use should be open for extension but closed for modification. This can be achieved with inheritance. You don’t have to touch the class you want to extend if you create a subclass of it. The original class is closed for modification but you can add custom code to your subclass to add new behavior.

Inheritance may be the most popular way to implement the Open-Closed Principle but it is not the only one. Objective-C, for example, offers categories which can be used to add methods to a class, without touching the original class and without subclassing it.

Example

Imagine you use an external library which contains a class Car. The Car has a method brake. In its base implementation, this method only slows down the car but you also want to turn on the brake lights. You would create a subclass of Car and override the method brake. After calling the original method of the super class, you can call your own turnOnBrakeLights method. This way you have extended the Car‘s behavior without touching the original class from the library.

 

Liskov Substitution Principle

子类 可以被 基类 替换。

即子类 需要 完全遵从 父类的定义, 不能修改其函数参数 以及 作用。

 

Liskov Substitution Principle

Derived classes must be substitutable for their base classes.

Assume we have a class B which is a subclass of A. If your program works with an object a of class A you can replace it with an object b of class B without changing the behavior of the program.

The implementation of this principle can be a little bit tricky if you combine it with the Open-Closed Principle. Even if you extend the behavior of your class in a subclass, you must make sure that you could still exchange the base class with the derived class without breaking anything.

Example

You have an instance of the class Car which your program uses to perform a drive action. This instance could be replaced by an instance of the class Tesla if Tesla is a subclass of Car.

 

 

Interface Segregation Principle

接口隔离原则, 对于一个类, 不应该定义 多种业务接口, 例如一个类饲养员, 有喂狗的方法, 和喂猪的方法, 

不应该将两个换在一个饲养员类上, 应该构造 狗饲养员 和 猪饲养员。

 

Interface Segregation Principle

Make fine grained interfaces that are client specific.

Classes should be as specialized as possible. You do not want any god classes that contain the whole application logic. The source code should be modular and every class should contain only the minimum necessary logic to achieve the desired behavior. The same goes for interfaces. Make small and specific interfaces so the client who implements them does not depend on methods it does not need. Instead of one class that can handle three special cases it is better to have three classes, one for each special case.

Example

In an adventure game, you have a class for your main character. The player can either be a warrior or an archer or a wizard. Instead of a class which can perform all the actions, like strike, shoot and heal, you would create three different classes, one for each character type. In the end, you would not only have a Character class but also a Warrior, an Archer and a Wizard, all inheriting from Character and implementing their specific actions.

 

Dependency Inversion Principle

依赖倒置, 关注结构层 和 上层逻辑的定义, 让 底层依赖上层。

不是上层直接饮用底层,形成的依赖。

Dependency Inversion Principle

Depend on abstractions, not on concretions.

Classes should not depend on concrete details of other classes. Even classes from a high domain level should not handle the specific details of components from a lower level. Both low and high level classes should depend on the same abstractions. To create specific behavior you can use techniques like inheritance or interfaces.

Example

Imagine we have a class Distributer which is able to share a blog post on different platforms. According to the Interface Segregation Principle the distributer uses a composition of several instances, like a TwitterShareAction and a FacebookShareAction. Now the goal of the Dependency Inversion Principle is to not depend on concrete methods of the share action classes, like sharePostOnTwitter and sharePostOnFacebook. Instead the Distributer class would define an interface called Sharing which is implemented by TwitterShareAction and FacebookShareAction. It declares the abstract method sharePost. The Distributer class doesn’t have to know the details of the concrete share actions. It just calls the sharePost method.

 

https://code-specialist.com/write-better-code/solid/

形象的例子。

The DIP violation here is that a switch is a concept that is logically in a layer above the light bulb, and the switch relies on it. This will lead to poor extensibility or even circular imports that prevent the program from being interpreted or compiled.

Violating the Dependency Inversion Principle. The controller module relies on the device module instead vice versaDependency Inversion Violation

Instead of the light bulb telling the switch how the bulb should be handled, the switch should tell the light bulb how to implement it. The naive approach would be to define an interface that tells the light bulb how it should behave to be used with a switch.

 

Visualized as a class diagram, this source code would lead to the object-oriented design:

Dependency Inversion Principle in an UML class diagram. A Device Interface as the solution for the switch not to rely on the light bulbDependency Inversion Solution

The dependency has been inverted. Instead of the switch relying on the light bulb, the light bulb now relies on an interface in a higher module. Also, both rely on abstractions, as required by the DIP. Last but not least, we also fulfilled the requirement “Abstractions should not depend upon details. Details should depend upon abstractions” – The details of how the device behaves rely on the abstraction (Device interface).