SOLID -- OOP design principles
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
. TheCar
has a methodbrake
. 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 ofCar
and override the methodbrake
. After calling the original method of the super class, you can call your ownturnOnBrakeLights
method. This way you have extended theCar
‘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 ofA
. If your program works with an objecta
of classA
you can replace it with an objectb
of classB
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 adrive
action. This instance could be replaced by an instance of the classTesla
ifTesla
is a subclass ofCar
.
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
andheal
, you would create three different classes, one for each character type. In the end, you would not only have aCharacter
class but also aWarrior
, anArcher
and aWizard
, all inheriting fromCharacter
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 aTwitterShareAction
and aFacebookShareAction
. Now the goal of the Dependency Inversion Principle is to not depend on concrete methods of the share action classes, likesharePostOnTwitter
andsharePostOnFacebook
. Instead theDistributer
class would define an interface calledSharing
which is implemented byTwitterShareAction
andFacebookShareAction
. It declares the abstract methodsharePost
. TheDistributer
class doesn’t have to know the details of the concrete share actions. It just calls thesharePost
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.
Dependency 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 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).