Loading

设计模式的七大原则

设计模式之七大编程原则

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

笔记总结来源于B站:B站尚硅谷——Java设计模式

一、单一职责原则

简而言之,在设计程序的过程中确保一个类或者方法只负责一个职责,或者说只执行一个功能的设计原则被称为单一职责原则


看如下代码:

public class Demo01{
    public static void main(String[] args){
        Animal animal = new Animal();
        animal.eat("小猫");
        animal.eat("老鼠");
        animal.eat("鱼"); //我吃我自己!!!?
    }
}


class Animal{
    void eat(String name){
        System.out.println(name+"在吃鱼!");
    }
}
小猫在吃鱼!
老鼠在吃鱼!
鱼在吃鱼!

根据以上代码,虽然写的很蠢但能反应出一个问题,Animal的家住海边,管的比较宽,导致了小猫、老鼠、鱼都是吃鱼。

这样的代码非常非常非常的不合理,鱼怎么能吃自己呢?

1.2 修改思路

Animal实在是太笼统了,因此他的职位也特别的多,我们需要细分下来到小猫、老鼠、鱼的类,继承或者去实现Animal里面的吃方法

这样子小猫的类只管小猫、老鼠的类只管老鼠、鱼的类只管鱼,遵守单一职责原则

public class Demo01{
    public static void main(String[] args){
        Animal animal = new Animal();
        animal.CatEat("小猫");
        animal.MouseEat("老鼠");
        animal.FishEat("鱼");
    }
}


class Animal{
    void CatEat(String name){
        System.out.println(name+"在吃鱼!");
    }

    void MouseEat(String name){
        System.out.println(name+"偷吃大米!");
    }

    void FishEat(String name){
        System.out.println(name+"吃虾米!");
    }
}
小猫在吃鱼!
老鼠偷吃大米!
鱼吃虾米!

二、接口隔离原则

简而言之,对于A类通过一个接口使用B类的时候,B类所实现的接口应该建立在最小接口上


image

根据以上类图编写如下代码:

public class Demo02 {
    public static void main(String[] args){

    }
}

interface Interface1{
    void operation1();
    void operation2();
    void operation3();
    void operation4();
    void operation5();
}

//创建A类实现Interface1类
class B implements Interface1{

    @Override
    public void operation1() {
        System.out.println("A operation1");
        
    }

    @Override
    public void operation2() {
        System.out.println("A operation2");
    }

    @Override
    public void operation3() {
        System.out.println("A operation3");
    }

    @Override
    public void operation4() {
        System.out.println("A operation4");
    }

    @Override
    public void operation5() {

        System.out.println("A operation5");
    }
    
}

//创建A类实现B类
class D implements Interface1{

    @Override
    public void operation1() {
        System.out.println("B operation1");
        
    }

    @Override
    public void operation2() {
        System.out.println("B operation2");
    }

    @Override
    public void operation3() {
        System.out.println("B operation3");
    }

    @Override
    public void operation4() {
        System.out.println("B operation4");
    }

    @Override
    public void operation5() {

        System.out.println("B operation5");
    }
    
}

class A{

    void function1(Interface1 b){
        b.operation1();
    }

    void function2(Interface1 b){
        b.operation2();
    }

    void function3(Interface1 b){
        b.operation3();
    }
}


class C{

    void function1(Interface1 d){
        d.operation1();
    }

    void function4(Interface1 d){
        d.operation4();
    }

    void function5(Interface1 d){
        d.operation5();
    }
}

我是个懒人,这一看就能看出问题,A类只用到了B类的三个方法,C类也只用到了D类的三个方法,但是BD两个类却把接口Interface的所有方法都实现了,代码有点过于臃肿

2.1修改思路

把原来的接口拆开来,拆成多个接口,类只需要去实现他会用到的方法接口就行了,在最小程度上实现接口隔离原则

image

修改代码如下

public class Demo02 {
    public static void main(String[] args){

    }
}

interface Interface1{
    void operation1();
}

interface Interface2{

    void operation2();
    void operation3();

}

interface Interface3{
    void operation4();
    void operation5();
}


class B implements Interface1,Interface2{

    @Override
    public void operation1() {
        System.out.println("A operation1");
        
    }

    @Override
    public void operation2() {
        System.out.println("A operation2");
    }

    @Override
    public void operation3() {
        System.out.println("A operation3");
    }
    
}


class D implements Interface1,Interface3{

    @Override
    public void operation1() {
        System.out.println("B operation1");
        
    }

    @Override
    public void operation4() {
        System.out.println("B operation4");
    }

    @Override
    public void operation5() {

        System.out.println("B operation5");
    }
    
}

class A{

    void function1(Interface1 b){
        b.operation1();
    }

    void function2(Interface2 b){
        b.operation2();
    }

    void function3(Interface2 b){
        b.operation3();
    }
}


class C{

    void function1(Interface1 d){
        d.operation1();
    }

    void function4(Interface3 d){
        d.operation4();
    }

    void function5(Interface3 d){
        d.operation5();
    }
}

三、依赖倒转原则

简而言之,在接收具体实现过程时应该交给实现类来完成,但传入参数可以用抽象类和接口来定义,从而设计在调用和传入中间过度一个缓冲层,给代码增加啊伸缩性以及可扩展性


不多bb看代码:

public class Demo03 {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}

class Email{
    String getInfo(){
        return "Hello, Email";
    }
}

class Person{

    void receive(Email msg){
        System.out.println(msg.getInfo());
    }
}

在这段通俗易懂的代码中,可以看到Person可以接收email的消息,但问题是现在都是2022年了,我想来一个接收飞鸽传书的方式,是不是得在重写一个方法,而且在传入参数直接传入了一个对象,这不是一个很好的选择(虽然我以前经常这么干)

3.1解决思路

根据上述问题,通过依赖倒转原则可以轻松得解决,甚至还很舒服,我看完只能说“66666666666666666666”

第一步我先创建一个Receiver的接口,我不管你是Email、微信、QQ、飞鸽传书哪怕是漂流瓶都得实现这个接口,然后在Person类里面的receive方法的参数msg类型改为Receiver,这样做的好处是:

  • 我可以传递任何实现接口Receiver的类
  • 如果我想增加一个facebook的消息只需要在写一个类去实现Receiver即可
  • 我觉得很有逼格
public class Demo03 {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
        person.receive(new FlyingPigeon());
        person.receive(new QQ());
    }
}

interface Receiver{
    String getInfo();
}

class Email implements Receiver{
    public String getInfo(){
        return "Hello, Email";
    }
}

class FlyingPigeon implements Receiver{
    public String getInfo(){
        return "Hello, I'm good boy";
    }
}

class QQ implements Receiver{
    public String getInfo(){
        return "Hello, You are my friend.";
    }
}

class Person{

    void receive(Receiver msg){
        System.out.println(msg.getInfo());
    }
}

Hello, Email
Hello, I'm good boy
Hello, You are my friend.

四、里氏替换原则

简而言之,子类尽量少重写父类的方法,一定要写可以通过聚合、组合、依赖来解决


public class Demo04 {
    public static void main(String[] args) {

        B b = new B();

        //在这里我以为我通过B类对象能够调用到A类的fun1
        System.out.println("11-3="+b.fun1(11, 3));
        System.out.println("1-8="+b.fun1(1, 8));
    }
}


class A{
    public int fun1(int a,int b){
        return a-b;
    }
}

class B extends A{

    //试图重载A类方法 但我不知道重载了
    public int fun1(int a,int b){
        return a+b;
    }

    public int fun2(int a,int b){
        return fun1(a,b)+9;
    }
}

在这段代码中有一个逻辑性的错误,在主方法中本意是通过B类对象去调用A类方法,但在B类中错误的将fun1的方法重写了,导致代码上的逻辑性错误,这一错误看似是一个很刻意的,但在实际开发中,很难会提示到自己从写了父类的方法然后在主函数中又错误的调用。

4.1解决思路

在视频课里面,这个例子举得多少有点牵强,并且创造了一个base类,让AB类去继承,但在一个没有更加基础属性和方法的情况下base类是可以不加的,直接在B类中调用A类的方法通过组合来解决这个问题(个人理解),如果后续还要增加一个两个类公共的东西可以创建base类

public class Demo04 {
    public static void main(String[] args) {

        B b = new B();
      
        System.out.println("11-3="+b.fun3(11, 3));
        System.out.println("1-8="+b.fun3(1, 8));
    }
}


class A{
    public int fun1(int a,int b){
        return a-b;
    }
}

class B{

    private A a = new A();

    public int fun1(int a,int b){
        return a+b;
    }

    public int fun2(int a,int b){
        return fun1(a,b)+9;
    }

    public int fun3(int a,int b){
        return this.a.fun1(a, b);
    }
}

五、开闭原则

对扩展功能开放,对修改功能关闭

public class Demo05 {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();

        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Rectangle());
    }
}


class GraphicEditor{

    public void drawShape(Shape s){
        if(s.m_type == 1){
            this.drawRect(s);
        }else if(s.m_type == 2){
            this.drawCircle(s);
        }
    }

    public void drawRect(Shape r){
        System.out.println("矩形");
    }

    public void drawCircle(Shape r){
        System.out.println("圆");
    }
}

class Shape{
    int m_type;
}

class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
}

class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
}

就不输出了,在上述代码中创建了一个画图形的类(GraphicEditor),里面有一个方法drawShape,根据传递进去的s的m_type来判断到底是画矩形还是画圆,如果我现在想增加一个画三角形的代码需要更改几处地方???

5.1解决思路

其实就是想打算在不修改GraphicEditor的情况下增加功能,这样我就可以对使用方关闭修改,在另外一处开辟一个拓展的功能,这里用到的跟依赖倒转原则很像,创建一个抽象类作为缓冲层,对外扩展可以新增实现这个抽象类的实现类

public class Demo05 {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();

        graphicEditor.drawShape(new Circle());
        graphicEditor.drawShape(new Rectangle());
    }
}

class GraphicEditor{

    public void drawShape(Shape s){
        s.draw();
    }

}

abstract class Shape{
    int m_type;

    abstract void draw();
}

class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }

    @Override
    void draw() {
        System.out.println("矩形");
    }

    
}

class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }

    @Override
    void draw() {
        System.out.println("圆");
    }

    
}

六、迪米特法则

也叫最少知道法则,陌生的类尽量不要以局部变量的形式出现,对依赖的类知道的越少越好

迪米特法则

七、合成复用原则

能用合成/聚合就不用继承

合成复用原则

posted @ 2022-09-29 21:17  StarVik  阅读(62)  评论(0编辑  收藏  举报