概述
1.单一原则
一个类或者模块只负责完成一个职责(或者功能),也就是说,不要设计大而全的类,要设计粒度小、功能单一的类。换个角度来讲就是,一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。
public class UserInfo {
private long userId;
private String username;
private String email;
private String telephone;
private String provinceOfAddress; // 省
private String cityOfAddress; // 市
private String regionOfAddress; // 区
private String detailedAddress; // 详细地址
}
上述的UserInfo是用来记录用户信息的类,从其中的信息看都是和用户相关的,是满足单一原则,但是其中的地址信息可以拆分出来作为单独的AddressInfo,UserInfo 只保留除 AddressInfo之外的其他信息,拆分之后的两个类的职责更加单一。实际开发中到底要不要拆分,我们应该从具体的应用场景去考量,例如微信中的地址信息,它只是用来展示,那么放在一起就是合理的,如果在淘宝中,因为地址信息还会用在物流中,那么就应该拆分出来作为单独的信息。
不同的应用场景、不同阶段的需求背景下,对同一个类的职责是否单一的判定,可能都是不一样的。在某种应用场景或者当下的需求背景下,一个类的设计可能已经满足单一职责原则了,但如果换个应用场景或着在未来的某个需求背景下,可能就不满足了,需要继续拆分成粒度更细的类。
看一个类的职责是否足够单一,并没有一个非常明确的、可以量化的标准,可以说,这是一个主观的认知。实际开发中,我们可以先不拆分写一个粗粒度的类,满足业务需求。随着业务的发展,如果粗粒度的类越来越庞大,代码越来越多,这个时候,我们就可以将这个粗粒度的类,拆分成几个更细粒度的类。满足单一原则。
到底要不要拆分可以从以下几点考量:、
1.类是否过大,主要是代码行数、函数或属性过多
类过大,会影响可读性和可维护性,这时候我们就应该考虑拆分,类的有效代码行数(注释空行除外)一般保持在300行左右,属性方法保持在10个以内比较合理
2.这个类依赖其它类,或者其它类依赖这个类过多
依赖或者被依赖过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分
单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。
2.对扩展开放、修改关闭
详细表述一下,那就是,添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。
public class StudentReport {
private String name;
private Integer englishScore; //英语成绩
private Integer mathematicsScore; //数学成绩
public StudentReport(String name, Integer englishScore, Integer mathematicsScore) {
this.name = name;
this.englishScore = englishScore;
this.mathematicsScore = mathematicsScore;
}
public String getGrade() {
String level = null;
if (englishScore >= 90 && mathematicsScore >= 90) {
level = "优秀";
} else if (englishScore >= 70 && mathematicsScore >= 70) {
level = "良好";
} else if (englishScore >= 60 && mathematicsScore >= 60) {
level = "及格";
} else if (englishScore < 60 || mathematicsScore < 60) {
level = "不及格";
}
return level;
}
}
上面这段代码非常简单,是一个学生成绩单包括英语和数学成绩,逻辑主要在getGrade,获取成绩等级,如果我们现在要将语文成绩也纳入成绩单,我们需要增加一个属性,并且修改getGrade逻辑
public class StudentReport {
private String name;
private Integer englishScore;
private Integer mathematicsScore;
private Integer languageScpre;
public String getGrade() {
String level = null;
if (englishScore >= 90 && mathematicsScore >= 90 && languageScpre>=90) {
level = "优秀";
} else if (englishScore >= 70 && mathematicsScore >= 70 && languageScpre>=70) {
level = "良好";
} else if (englishScore >= 60 && mathematicsScore >= 60 && languageScpre>=60) {
level = "及格";
} else if (englishScore < 60 || mathematicsScore < 60 ||languageScpre<60) {
level = "不及格";
}
return level;
}
}
这样的代码修改实际上存在挺多问题的。一方面,我们对接口进行了修改,这就意味着调用这个接口的代码都要做相应的修改。另一方面,修改了 check() 函数,相应的单元测试都需要修改,这就不符合对扩展开放,对修改关闭原则,我们对代码就行重构。
public class GradeInfo {
private String name;
private Integer englishScore;
private Integer mathematicsScore;
private Integer languageScpre;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getEnglishScore() {
return englishScore;
}
public void setEnglishScore(Integer englishScore) {
this.englishScore = englishScore;
}
public Integer getMathematicsScore() {
return mathematicsScore;
}
public void setMathematicsScore(Integer mathematicsScore) {
this.mathematicsScore = mathematicsScore;
}
public Integer getLanguageScpre() {
return languageScpre;
}
public void setLanguageScpre(Integer languageScpre) {
this.languageScpre = languageScpre;
}
}
public interface GradleHandle {
public String check(GradeInfo info);
}
public class ExcellentHandler implements GradleHandle{
@Override
public String check(GradeInfo info) {
if(info.getEnglishScore()>90 && info.getLanguageScpre()>90 && info.getMathematicsScore()>90) {
return "优秀";
}
return null;
}
}
public class GoodHandle implements GradleHandle{
@Override
public String check(GradeInfo info) {
if(info.getEnglishScore()>70 && info.getLanguageScpre()>70 && info.getMathematicsScore()>70) {
return "良好";
}
return null;
}
}
public class Grade {
private List<GradleHandle> gradleHandles = new ArrayList<GradleHandle>();
public void addGradleHandle(GradleHandle gradleHandle) {
this.gradleHandles.add(gradleHandle);
}
public String grade(GradeInfo info) {
for (GradleHandle handler : gradleHandles) {
String check = handler.check(info);
if(check!=null && !check.isEmpty()) {
return check;
}
}
return null;
}
}
重构之后的代码更加灵活和易扩展。
3.里式替换原则
派生类(子类)对象可以在程序中代替其基类(超类)对象
里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。举例说明:
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserHandle {
public void save(String name ,Integer id) {
User user = new User();
if(name!=null && !name.isEmpty()) {
user.setName(name);
}
if(id!=null) {
user.setId(id);
}
System.out.println("save user");
}
}
public class UserHandleSave extends UserHandle{
public void save(String name ,Integer id) {
User user = new User();
if(name!=null && !name.isEmpty()) {
user.setName(name);
}
if(id!=null) {
throw new RuntimeException();
}
System.out.println("save user");
}
public static void main(String[] args) {
UserHandle uh = new UserHandleSave();
uh.save("xxx",1);
}
}
上述就不符合里式替换原则,违背了父类对异常的约定
4.接口隔离原则
客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成更小具体的接口,这样客户讲会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署。
假如有一个用户系统,用户信息只允许后台员修改,其他人员没有权限修改,我们接口设计如下:
public interface UpdateUser {
public void save(User user);
public void update(User user);
}
public interface ViewUser {
public User get(Integer id);
public List<User> query(String name);
}
public class UserService implements UpdateUser,ViewUser {
@Override
public void save(User user) {
}
@Override
public void update(User user) {
}
@Override
public User get(Integer id) {
return null;
}
@Override
public List<User> query(String name) {
return null;
}
}
如果有部分调用者只需要接口中的部分功能,那我们就需要把接口拆分成粒度更细的多个即接口,让调用者只依赖它需要的那个细粒度接口
5.依赖倒置原则
在面向对象领域中,依赖反转原则(Dependency inversion principle,DIP)是指一种特定的解耦(传统的依赖关系创建在高层次上,而具体的策略设置则应用在低层次的模块上)形式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。
该原则规定:
- 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
- 抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。
该原则颠倒了一部分人对于面向对象设计的认识方式。如高层次和低层次对象都应该依赖于相同的抽象接口
public interface MessageSender {
public void send(String msg);
}
public class MessageSenderImpl implements MessageSender{
@Override
public void send(String msg) {
System.out.println("send msg");
}
}
public class Notification {
MessageSender messageSender;
public void setMessageSender(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void send() {
messageSender.send("xxx");
}
public static void main(String[] args) {
Notification notification = new Notification();
MessageSender messageSender = new MessageSenderImpl();
notification.setMessageSender(messageSender);
notification.send();
}
}
6.迪米特法则
每个模块(unit)只应该了解那些与它关系密切的模块(units: only units “closely” related to the current unit)的有限知识(knowledge)。或者说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”
通俗的说就是不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口(也就是定义中的“有限知识”)。案例如下:
public interface Serializable {
String serialize(Object object);
}
public interface Deserializable {
Object deserialize(String text);
}
public class Serialization implements Serializable, Deserializable {
@Override
public String serialize(Object object) {
String serializedResult = "";
return serializedResult;
}
@Override
public Object deserialize(String str) {
Object deserializedResult = "...";
return deserializedResult;
}
}
public class Demo1 {
Serializable serializable;
public Demo1(Serializable serializable) {
this.serializable = serializable;
}
}
public class Demo2 {
private Deserializable deserializer;
public Demo2(Deserializable deserializer) {
this.deserializer = deserializer;
}
}
高内聚、松耦合”是一个非常重要的设计思想,能够有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中。所谓松耦合指的是,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少
最后
以上就是尊敬小鸭子为你收集整理的设计模式—设计原则的全部内容,希望文章能够帮你解决设计模式—设计原则所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复