-
单一职责原则(Single Responsibility Principle,SRP)
- 定义:一个类应该只有一个引起它变化的原因。也就是说,一个类只负责一项职责。
- 示例与解释:例如,有一个
UserService
类,它的职责如果包括用户的注册、登录以及用户信息的修改。这就不符合单一职责原则,因为用户注册和登录主要涉及认证相关的功能,而用户信息修改涉及数据更新的功能。更好的做法是将其拆分为UserAuthenticationService
(负责用户认证相关功能)和UserInfoService
(负责用户信息更新功能)两个类。这样,当认证相关的需求(如添加新的登录方式)发生变化时,只需要修改UserAuthenticationService
类;当用户信息更新的规则(如添加新的信息字段)改变时,只需要修改UserInfoService
类,而不会相互影响。 - 优点:降低类的复杂度,提高类的可读性和可维护性,并且更容易进行单元测试,因为每个类的职责明确,测试用例的编写和维护也更加简单。
-
开闭原则(Open - Closed Principle,OCP)
- 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即当需要对一个软件实体进行功能扩展时,应该通过扩展新的代码来实现,而不是修改原有的代码。
- 示例与解释:以图形绘制系统为例,有一个
Shape
抽象类,它有draw
方法,并且有Circle
和Rectangle
等具体形状类实现了这个方法来绘制自己的形状。如果要添加一个新的形状(如Triangle
),应该是创建一个新的Triangle
类来实现Shape
类的draw
方法,而不是修改Shape
类或者已有的Circle
和Rectangle
类的draw
方法。这样,在不修改原有代码的基础上,通过添加新的类就实现了功能的扩展,符合开闭原则。 - 优点:使得软件系统具有更好的可扩展性和可维护性。当软件系统需要增加新功能时,不需要担心修改原有代码可能带来的风险,如引入新的错误或者影响已有的功能。
-
里氏替换原则(Liskov Substitution Principle,LSP)
- 定义:所有引用基类(父类)的地方必须能透明地使用其子类的对象。也就是说,子类对象能够替换父类对象,并且程序的行为不会发生改变。
- 示例与解释:假设有一个
Animal
基类,有一个makeSound
方法,Dog
和Cat
是Animal
的子类。在一个函数printAnimalSound(Animal animal)
中,它调用animal.makeSound()
方法来打印动物的声音。根据里氏替换原则,Dog
和Cat
类重写makeSound
方法后,在printAnimalSound
函数中,无论是传入Dog
对象还是Cat
对象,函数都应该能够正常工作,并且输出符合预期的动物叫声。如果Dog
类重写makeSound
方法后,执行的不是打印狗叫的声音,而是做了其他事情,就违反了里氏替换原则。 - 优点:保证了继承关系的正确性和一致性,使得代码在使用多态性时能够按照预期工作,增强了程序的健壮性和可维护性。
-
依赖倒置原则(Dependency Inversion Principle,DIP)
- 定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
- 示例与解释:在一个简单的用户数据存储系统中,有一个
UserRepository
接口作为抽象,它定义了存储用户数据的基本方法(如saveUser
、getUser
等)。有MySQLUserRepository
和MongoDBUserRepository
两个具体实现类,分别用于将用户数据存储到MySQL数据库和MongoDB数据库。高层模块(如UserService
)应该依赖UserRepository
这个抽象,而不是直接依赖MySQLUserRepository
或者MongoDBUserRepository
这些细节。这样,当需要更换数据库或者添加新的数据库存储方式时,只需要创建新的实现UserRepository
接口的类,而不会影响到UserService
这个高层模块。 - 优点:降低了模块之间的耦合度,使得系统更加灵活和易于维护,便于进行单元测试和模块替换。
-
接口隔离原则(Interface Segregation Principle,ISP)
- 定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
- 示例与解释:假设有一个
Worker
接口,它定义了doWork
、takeBreak
、attendMeeting
等方法。但对于一些只负责实际工作的工人(如生产线上的工人),他们不需要attendMeeting
这个功能。更好的做法是将Worker
接口拆分为ProductiveWorker
接口(包含doWork
和takeBreak
方法)和ManagerialWorker
接口(包含attendMeeting
等管理相关方法)。这样,生产线上的工人只需要实现ProductiveWorker
接口,避免了实现不需要的attendMeeting
方法,降低了类与接口之间的耦合度。 - 优点:提高了接口的内聚性,减少了客户端需要实现的不必要的方法,使得接口的使用更加灵活和易于维护。
-
迪米特法则(Law of Demeter,LoD),也称为最少知识原则(Least Knowledge Principle)
- 定义:一个对象应该对其他对象有最少的了解。也就是说,一个类应该尽量减少对其他类的依赖,只和与自己有直接关系的类进行交互。
- 示例与解释:例如,有一个
Order
类,它包含Customer
信息和Product
信息。如果Order
类需要计算订单总价,不应该直接访问Product
类中的供应商信息来获取产品成本等数据来计算价格,因为Order
类和供应商信息没有直接关系。应该是Product
类提供一个获取产品价格的方法,Order
类通过调用这个方法来获取产品价格,然后计算总价。这样就限制了Order
类对其他类的了解范围,符合迪米特法则。 - 优点:降低了类之间的耦合度,使得系统的维护和扩展更加容易,因为一个类的变化对其他类的影响范围更小。
-
组合/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)
- 定义:尽量使用组合(has - a关系)或聚合(owns - a关系)关系来实现复用,而不是使用继承关系。
- 示例与解释:假设有一个
Car
类,它需要有一个导航系统的功能。可以通过继承一个NavigationSystem
类来实现,但这会导致Car
类和NavigationSystem
类的耦合度很高。更好的做法是将NavigationSystem
作为Car
类的一个成员变量(组合关系),这样Car
类可以使用NavigationSystem
的功能,而且当NavigationSystem
类发生变化时,不会像继承关系那样直接影响Car
类。如果有多个类需要导航系统功能,也可以通过这种组合方式复用NavigationSystem
,而不会像继承那样产生复杂的类层次结构。 - 优点:使得系统更加灵活,降低了类之间的耦合度,并且可以在运行时动态地改变组合或聚合对象的行为,有利于代码的复用和维护。
人工智能写的比我好
标签:设计模式,原则,示例,接口,Principle,遵循,软件,应该,方法 From: https://www.cnblogs.com/euler-blog/p/18613538