![solid设计原则](https://i-blog.csdnimg.cn/blog_migrate/32d7883175f5b05eca20472e249013d4.png)
solid设计原则
介绍:
Robert C. Martin定义了五项面向对象的设计原则:
- 小号英格尔-责任原则
- Ø笔封闭原则
- 大号iskov的替换原则
- 我覆盖整个院落分离原则,并
- d ependency倒置原则
这些共同被称为SOLID原则。 在设计面向对象的系统时,我们应该尽可能地遵循这些原则。 这些原则有助于我们设计一个更可扩展,可理解和可维护的系统。
随着应用程序规模的扩大,使用这些原则可以帮助我们节省很多工作。
单一责任原则:
顾名思义,“单一责任原则”(SRP)指出,每个类都必须完全做一件事。 换句话说,我们修改类的原因不应多于一个。
众所周知,大型系统通常具有数千个类。 如果对于任何新要求,需要涉及多个类,那么我们就有更多机会通过破坏另一种功能来引入错误。
单一责任原则为我们带来以下好处:
- 更少的耦合:由于每个类都只会做一件事,因此依赖关系将大大减少
- 易于测试:更少的测试用例可以覆盖整个系统,从而使代码更易于测试
我们系统的模型类通常始终遵循SRP原则。 可以这么说,我们需要修改系统中用户的状态,我们只需触摸User类:
public class User {
private int id;
private String name;
private List<Address> addresses;
//constructors, getters, setters
}
因此,它遵循SRP原则。
开闭原理:
开放式封闭原则指出,软件组件必须开放才能扩展,但必须封闭才能修改。 此处的目的是避免由于代码修改而破坏某些现有的工作功能,从而避免在系统中引入错误。 我们应该扩展现有的类以支持任何其他功能。
此规则适用于我们的系统中更稳定的类,这些类已通过测试阶段并且在生产中运行良好。 我们将希望避免破坏现有代码中的任何内容,因此我们应该扩展其支持的功能以满足新的需求。
假设我们的系统中有一个EventPlanner类,该类在生产服务器上长期运行良好:
public class EventPlanner {
private List<String> participants;
private String organizer;
public void planEvent() {
System.out.println( "Planning a simple traditional event" );
...
}
...
}
但是现在,我们计划改为使用ThemeEventPlanner ,它将使用随机主题来计划事件,以使事件变得更加有趣。 与其直接跳入现有代码并添加逻辑以选择事件主题并使用它,不如扩展我们的生产稳定类:
public class ThemeEventPlanner extends EventPlanner {
private String theme;
...
}
对于大型系统,确定类的所有用途并不是很简单。 因此,通过仅扩展功能,我们减少了处理系统未知数的机会。
里斯科夫的替代原则:
Liskov的“替换原理”说,派生类型必须能够完全替代其基本类型,而不改变现有行为。 因此,如果我们有两个类A和B使得B扩展了A,那么我们应该能够在整个代码库中用B替换A而不影响系统的行为。
为了使我们能够做到这一点,子类的对象的行为必须与超类对象的行为完全相同。
该原则有助于我们避免类型之间的错误关系,因为它们可能导致意外的错误或副作用。
让我们看下面的例子:
public class Bird {
public void fly() {
System.out.println( "Bird is now flying" );
}
}
public class Ostrich extends Bird {
@Override
public void fly() {
throw new IllegalStateException( "Ostrich can't fly" );
}
}
尽管鸵鸟是鸟,但它仍然不能飞行,因此这明显违反了Liskov替代原理(LSP)。 同样,涉及类型检查逻辑的代码清楚地表明已建立了不正确的关系。
重构代码以遵循LSP有两种方法:
- 消除对象之间的错误关系
- 使用“告诉,不要问”原则消除类型检查和转换
假设我们有一些涉及类型检查的代码:
//main method code
for (User user : listOfUsers) {
if (user SubscribedUser) { (user instanceof SubscribedUser) {
user.offerDiscounts();
}
user.makePurchases();
}
使用“告诉,不要问”的原则,我们将重构上面的代码,使其看起来像:
public class SubscribedUser extends User {
@Override
public void makePurchases() {
this .offerDiscounts();
super .makePurchases();
}
public void offerDiscounts() {...}
}
//main method code for (User user : listOfUsers) {
user.makePurchases();
}
接口隔离原理:
根据接口隔离原则,不应强迫客户端处理他们不使用的方法。 我们应该在需要时将较大的接口拆分为较小的接口。
假设我们有一个ShoppingCart界面:
public interface ShoppingCart {
void addItem(Item item);
void removeItem(Item item);
void makePayment();
boolean checkItemAvailability(Item item);
}
付款和检查商品的可用性不是购物车要执行的操作。 我们很可能会遇到不使用这些方法的该接口的实现。
因此,最好将上述接口改为:
public interface BaseShoppingCart {
void addItem(Item item);
void removeItem(Item item);
}
public interface PaymentProcessor {
void makePayment();
}
public interface StockVerifier {
boolean checkItemAvailability(Item item);
}
接口隔离原则(ISP)还加强了其他原则:
- 单一职责原则:实现较小接口的类通常更加集中并且通常具有单一目的
- Liskov替换原理:使用较小的接口,我们有更多的机会让类实现它们以完全替代接口
依赖倒置:
它是最流行和有用的设计原则之一,因为它促进了对象之间的松散耦合。 依赖倒置原则指出高级模块不应该依赖于低级模块; 两者都应取决于抽象。
高级模块告诉我们该软件应该做什么。 用户授权和付款是高级模块的示例。
另一方面,低级模块告诉我们该软件应如何执行各种任务,即它涉及实现细节。 低级模块的一些示例包括安全性(OAuth),网络,数据库访问,IO等。
让我们编写一个UserRepository接口及其实现类:
public interface UserRepository {
List<User> findAllUsers();
}
public class UserRepository implements UserRepository {
public List<User> findAllUsers() {
//queries database and returns a list of users
...
}
}
我们在这里提取了接口中模块的抽象。
现在说我们有高级模块UserAuthorization ,它检查用户是否被授权访问系统。 我们将仅使用UserRepository接口的引用:
public class UserAuthorization {
...
public boolean isValidUser(User user) {
UserRepository repo = UserRepositoryFactory.create();
return repo.getAllUsers().stream().anyMatch(u -> u.equals(user));
}
}
另外,我们使用工厂类来实例化UserRepository 。
请注意,我们仅依靠抽象而不是依赖。 因此,我们可以轻松添加UserRepository的更多实现,而对我们的高级模块没有太大影响。
多么优雅!
结论:
在本教程中,我们讨论了SOLID设计原则。 我们还查看了上述每种原则的Java代码示例。
翻译自: https://www.javacodegeeks.com/2019/09/solid-design-principles.html
solid设计原则