java 七大设计原则之依赖倒置,里氏替换原则(文字代码相结合理解)

314 阅读7分钟

java 七大设计原则之依赖倒置,里氏替换原则,文字代码相结合理解

喜欢就争取,得到就珍惜,错过就忘记。人生也许不尽完美,正因为不完美,我们才需要不断地努力创造努力奋斗。时间就是生命,所以我们必须珍惜宝贵的生命,执着地守候生命中每一个必经的十字路口。

七大设计原则有哪些?

  • 单一职责原则
  • 接口隔离原则
  • 依赖倒转(倒置)原则
  • 里氏替换原则
  • 开闭原则
  • 迪米特法则
  • 合成复用原则

通常大家理解的是前六个,并没有合成复用原则

为什么要使用七大设计原则?

  • 代码重用性(相同的代码不用多次编写);
  • 可读性(编程的规范性,便于其他程序员的阅读和理解);
  • 可拓展性(当需要添加新的功能时,非常的方便也称为可维护性);
  • 可靠性(当我们添加新的功能后对原来的功能没有影响);
  • 使程序呈现高内聚,低耦合等特性

依赖倒置原则

依赖倒置原则定义:

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象
  • 依赖倒转原则的中心思想就是面向接口编程
  • 使用接口或者抽象类的目的是制定好规范,而不涉及任何的具体操作,把细节的任务交给实现类去完成

依赖倒转原则是基于这样的设计理念:相对于细节得多变性,抽象的东西要稳定的多。以抽象为基础搭建的框架比细节为基础的架构要稳定得多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类

普通代码:

//输出消息
public class QQNews {
    public  void  run(){
        Log.i("Inversion","我是 QQ 发送的消息 ");
    }
}
//接收消息
public class Information {
    public void showInfo(QQNews qqNews){
        qqNews.run();
    }
}

//使用代码:
Information information = new Information();
information.showInfo(new QQNews());

问题:

  • 直接与类之间交互,传输消息比较固定
  • 如果我现在想接收微信消息他是没办法接收的,因为代码已经固定了,现在只接受QQ的消息,如果需要接收微信的消息,还需要在Information类中重新写接收微信消息的方法.
  • 没有’缓冲层’,showInfo()方法直接与QQNews类交互,'弹性度’太低
  • 没有遵守依赖倒置原则,依赖倒转原则的中心思想就是面向接口编程

遵守依赖倒置原则代码:

    ///接收的消息
public interface Iinversion {
    void run();
}
//QQ发送消息
public class QQNews implements Iinversion {
    @Override
    public void run() {
        Log.i("Inversion", "我是 QQ 发送的消息");
    }
}
//微信发送消息
public class WeChatNews  implements Iinversion{
    @Override
    public void run() {
        Log.i("Inversion","我是 微信 发送的消息");
    }
}

代码使用:

InversionBean inversionBean = new InversionBean();
inversionBean.run(new QQNews());
inversionBean.run(new WeChatNews());

优点:

  • 客户端(InversionBean)只与Iinversion(接口)之间交互,耦合性降低了,而且准守了依赖倒置中面向接口编程原则
  • 不会像上面一样代码固定,也不需要判断任何条件,我传入QQ就接收QQ的消息,传入微信就接收微信的消息.相对于细节得多变性,抽象的东西要稳定的多,细节就是具体实现类

依赖倒置原则三种方法:

方式一:

通过传递接口(Iinversion)的实现类(QQNews)完成接口的传递

public interface Iinversion {
    ///接收的消息
    void run();
}
public class QQNews implements Iinversion {
    @Override
    public void run() {
        Log.i("Inversion", "我是 QQ 发送的消息");
    }
}

Information information = new Information();
information.showInfo(new QQNews());

方式二:

通过有参构造方法,完成接口的传递

public interface Iinversion {
    ///接收的消息
    void run();
}

public class Information {

   Iinversion mIinversion;
   
    public Information(Iinversion iinversion) {
        mIinversion = iinversion;
    }
    public void showInfo(){
        mIinversion.run();
    }
}
//使用代码
Information information = new Information(new QQNews());
information.showInfo();

方式三:

通过set方法完成接口传递:

public interface Iinversion {
    ///接收的消息
    void run();
}
//传递消息 
public class QQNews implements Iinversion {
    @Override
    public void run() {
        Log.i("Inversion", "我是 QQ 发送的消息");
    }
}

public class Information {

    private Iinversion mIinversion;

    public void showInfo(){
        mIinversion.run();
    }
    public void setIinversion(Iinversion iinversion) {
         mIinversion =iinversion;
    }
}

//使用代码:
Information information = new Information();
information.setIinversion(new QQNews());
information.showInfo();

里氏替换原则

基本介绍:

  • 里氏替换原则是1988年麻省理工学院一位姓里的女性提出

里氏替换原则定义:

  • 在继承时,遵守里氏替换原则,在子类中尽量不要去重写父类的方法
  • 所有引用基类的地方必须能透明地使用其子类的对象
  • 里氏替换原则告诉我们,继承实际上让两个类的耦合性增强,在适当的情况下,可以通过聚合,组合,依赖来解决问题
  • 程序中的父类可以被子类替换

未遵守里氏替换原则代码:

public class ReplaceA {
    public int show(int a, int b){
        return  a+b;
    }
}

public class ReplaceB extends ReplaceA{
    @Override
    public int show(int a, int b){
        return  a-b;
    }
}

//使用代码:
 //里氏替换原则
ReplaceA replaceA = new ReplaceA();
ReplaceB replaceB = new ReplaceB();

Log.i("LiReplace","2 + 3 = "+replaceA.show(2,3)+"");
Log.i("LiReplace","2 + 3 = "+replaceB.show(2,3)+"");

可以看出:

  • 在A类中输出的是 a + b 的和
  • 在B类中重写了A类的show()方法,输出的是a-b

问题:

  • 因为B类继承了A类,所以A类中的方法B类都可以使用,那么就导致了B类耦合性非常高
  • 而且如果说A类新增加一些方法,B类用不到,也会导致耦合性提高,如果说B类用不到,还能调用的话,是不是提高了B类的入侵性
  • 在这段代码中,我replaceB的本意是调用父类的show()方法求和,可是我忘记我重写了父类的show()方法求成差了!

假设一:
现在我现在忘记B类重写了A类的方法,我使用B类中的show()我想要的结果是求和,但是呢,B类重写了A类的方法,给我算成求差了,可能大家看看代码就能找出问题,那我在举一个例子.

假设二:现在有BCD三个类,都继承自A类

如果说我现在更改需求,我要C类求乘法,D类求除法,那又该怎么写呢?一个一个判断吗?而且这样写耦合性太高了,现在A,B,C,D这四个类感觉已经有点混乱了,这才是一个方法,如果有百八十个那不是直接凉凉了o(╥﹏╥)o

在来看看遵守里氏替换原则的写法:

遵守里氏替换原则的写法:

解决思路:
让A类和B类共同继承一个更通俗的父类(BaseReplace)

public class BaseReplace {
}

public class ReplaceA extends BaseReplace{
    public int show(int a, int b){
        return  a+b;
    }
}

public class ReplaceB extends BaseReplace{
    public int show(int a, int b){
        return  a-b;
    }
}

UML图(2.1):

分析:

  • A类和B类完全没有关系,完全是两个单独的模块,
  • 耦合性完全没有

如果在B类中使用A类的方法:

通过组合/聚合的方式:

public class ReplaceB extends BaseReplace{
    ReplaceA replaceA = new ReplaceA();
    
    public int show(int a, int b){
        return  a-b;
    }

    public void useAshow(int a,int b ){
        replaceA.show(a,b);
    }
	public class ReplaceA extends BaseReplace{
	    public int show(int a, int b){
	        return  a+b;
	    }
	}
}
//使用代码
ReplaceB replaceB = new ReplaceB();
replaceB.useAshow(2,3)

可以看到在B类中使用组合的方法 ,依然可以使用到A类的方法

这就是里氏替换原则

依赖倒置原则

里氏替换原则

猜你喜欢:

去设计模式/设计原则目录页

原创不易,记得点个赞哦~