设计原则大汇总
源代码地址
单一职责原则(Single responsibility principle)
SRP单一职责原则就是说,一个类他只负责一件事情。这样子就比较好维护,不然的话,一个类里面就会有很多逻辑(if/else)堆在一起,变成一个屎山类。
比如对不同的用户类型,系统提供不同的功能
错误示范,这个代码把所有用户类型的功能逻辑都放在了一个类里面,然后使用if/else来判断。当之后要修改或者添加用户功能的时候,维护就会很复杂。违背了SRP原则
public class BadDemo {
public void userType(String userType){
if(userType.equals("游客")){
System.out.println("请先登录");
}else if(userType.equals("普通用户")){
System.out.println("要看广告");
}else{
System.out.println("免广告");
}
}
}
正确示范,我们首先定义了一个接口,用来表示用户的行为。然后使用不同的实现类来具体实现用户逻辑,这样之后需要新增一个用户类型就只需要多建一个实现类。修改某个用户的逻辑只需要修改对应的实现类就行了。
//UserType.java
public interface UserType {
void userTypeAction();
}
//NormalUser
public class NormalUser implements UserType{
@Override
public void userTypeAction() {
System.out.println("要看广告");
}
}
//UnloginUser
public class UnloginUser implements UserType{
@Override
public void userTypeAction() {
System.out.println("请先登录");
}
}
//VipUser
public class VipUser implements UserType{
@Override
public void userTypeAction() {
System.out.println("免广告");
}
}
开闭原则(OpenClosedPrinciple)
开闭原则就是对扩展开放对修改关闭,可能听起来有点抽象我们举个例子来看。
比如说你定义了一个接口Circular 来计算圆的面积和周长,那么你肯定要在接口里面定义一个静态变量π。,大家都是用同一个实现类CircularImp。
对修改关闭
大家用这个CircularImp类的用的很舒服,但是突然有一天有个人发现π=3.14不够精确,他想要让π=3.1415926。如果他直接修改接口Circular里面的值,那么所有用这个类的人都会收到影响,其他人并不想要这么精确的π。所以一般来说不会直接对这个接口进行修改,而是对他进行扩展。这就是对修改关闭!
public interface Circular {
static final double pai=3.14;
public double getS(double r);
public double getP(double r);
public void roll();
}
public class CircularImp implements Circular {
@Override
public double getS(double r) {
return r * r * pai;
}
@Override
public double getP(double r) {
return 2 * pai * r;
}
@Override
public void roll() {
System.out.println("我会滚");
}
}
对扩展开放
既然想要用到精确的π,又不能直接修改原来的类(因为这个例子比较简单所以直接在原来的类那里加个传参就行,但是复杂的情况下就还是要扩展)。因此我们就new一个新的类CircularExt来继承原来的类就好了。然后重写两个需要π的方法,roll就不用重写可以保留。
public class CircularExt extends CircularImp {
private static final double pai = 3.1415926;
@Override
public double getP(double r) {
return 2 * pai * r;
}
@Override
public double getS(double r) {
return r * r * pai;
}
}
测试
public class test {
public static void main(String[] args) {
Circular c1=new CircularImp();
Circular c2=new CircularExt();
System.out.println(c1.getS(1));
c1.roll();
System.out.println(c2.getS(1));
c2.roll();
}
}
输出:
3.14
我会滚
3.1415926
我会滚
里氏替换原则(LSP)
简单的说里氏替换原则就一句话,就是在任何地方把一个父类替换成他的子类,代码都可以正常的运行,也就是继承的时候要保证超类的特性在子类中依然成立。
里氏替换原则主要是为了防止我们在代码里面乱继承,然后把代码搞得很乱,变得乱七八糟的。
比如 交通工具类Vehicle
,汽车类Car
,自行车类Bike
,很明显我们不能让Bike
继承Car
public class Vehicle {
public void drive(){
System.out.println("行驶");
}
}
public class Car extends Vehicle{
public void use(){
System.out.println("用油");
}
}
public class Bike extends Vehicle{
public void use(){
System.out.println("脚蹬");
}
}
迪米特原则
类于类之间要低耦合,也就是说要减少每个类之间的相互了解。让系统的功能模块更加的独立,相互之间没有或者尽量少的依赖。
强调只与你的直接朋友交谈,不跟“陌生人”说话。在软件设计中,如果两个实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。从而降低类间的耦合,增强类的独立性。
举例
比如你现在是一个老板Boss,然后你手下有一个销售部门SellDepartment,部门有一个部门主管SellLeader。根据迪米特法则,要减少每个类之间的相互了解那么当老板想要知道这个月销售数据的时候就应该去问销售部门主管,而不是一个个问销售部门的员工。类的调用关系应该是 Boss->SellLeader->SellDepartment
public class SellDepartment {
private List<Integer> sellNumber;
private List<String> employee;
public List<Integer> getSellNumber() {
return sellNumber;
}
public void setSellNumber(List<Integer> sellNumber) {
this.sellNumber = sellNumber;
}
public List<String> getEmployee() {
return employee;
}
public void setEmployee(List<String> employee) {
this.employee = employee;
}
}
public class SellLeader {
private SellDepartment sellDepartment;
public SellDepartment getSellDepartment() {
return sellDepartment;
}
public void setSellDepartment(SellDepartment sellDepartment) {
this.sellDepartment = sellDepartment;
}
public int getSellNum() {
List<Integer> sellNum = sellDepartment.getSellNumber();
int res = 0;
for (Integer integer : sellNum) {
res += integer;
}
return res;
}
}
public class Boss {
private SellLeader sellLeader;
public SellLeader getSellLeader() {
return sellLeader;
}
public void setSellLeader(SellLeader sellLeader) {
this.sellLeader = sellLeader;
}
public void getSellNum() {
System.out.println(sellLeader.getSellNum());
}
}
接口隔离原则(ISP)
该原则提倡精简的、具体的接口,而不是臃肿的、复杂的接口。换言说就是使用多个专门的接口,而不使用单一的总接口。
举例
假设你要有一个交通工具的接口Vehicle
,其中定义了两个方法drive()
,oiling(int money)
。
public interface Vehicle{
void drive();//驾驶
void oiling(int money);//加油
}
然后所有的具体交通工具的实现类都要实现这个接口。那么问题来了,Bike
自行车类并不需要加油。因此我们应该拆分Vehicle
接口把他变成机动车类和非机动车类。然后根据实现类的具体情况来选择实现哪个接口。
依赖倒置原则(DIP)
依赖导致原则的核心就是面向接口编程,高层模块不应该依赖低层模块,二者都应该依赖其抽象。使用接囗或抽象类的目的是制定好规范,而不涉及任何具体的搡作,把展现细节的任务交给他们的实现类去完成。
错误举例
假设现在有一个大厨类Cook
,他会做番茄Tomato
和土豆Potato
这两道菜
public class Cook {
public void cookTometo(Tomato tomato) {
tomato.cook();
}
public void cookPotato(Potato potato){
potato.cook();
}
}
public class Tomato {
public void cook(){
System.out.println("做番茄");
}
}
public class Potato {
public void cook(){
System.out.println("做土豆");
}
}
如果大厨又学会了一道新菜,哪个就需要在Cook
类中增加新的方法,代码的耦合度较高。
正确示范
首先我们把厨师要做的菜抽象成Food
接口,Tomato
和Patato
来实现Food
,厨师Cook
只负责烹饪Food
,不管他具体是什么菜。具体的烹饪过程有Food
的实现类来完成。
public interface Food {
void cook();
}
public class Tomato implements Food{
public void cook(){
System.out.println("做番茄");
}
}
public class Potato implements Food{
public void cook(){
System.out.println("做土豆");
}
}
public class Cook {
public void cook(Food food) {
food.cook();
}
}
这样之后厨师又要做什么新菜了只需要在多一个实现类就行了