设计模式:六大原则

一、单一职责原则

意思是每个类都只负责单一的功能,切不可太多,并且一个类应当尽量的把一个功能做到极致。

违背该原则代码示例:

package com.whl.sixPrinciple;

import java.io.*;

/**
 * Author heling on 2018/11/27
 */
public class Caculator {

    public int add() throws IOException {
        File file = new File("E:\\Caculator.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        Integer a = Integer.valueOf(br.readLine());
        Integer b = Integer.valueOf(br.readLine());
        return a + b;
    }

    public static void main(String[] args) throws IOException {
        Caculator caculator = new Caculator();
        int result = caculator.add();
        System.out.println(result);
    }
}

存在问题:

当我们需要在计算器类添加减法,乘法,除法等其它算法的时候,我们必须要复制读取a和b数据的方法,并且新加算法,造成代码冗余,难以维护。

分析一下,这个类做了两件事情:1.获取文件里的数据、2.计算。下面我们根据单一职责原则重新设计一下:

数据读取类:

package com.whl.sixPrinciple;

import java.io.*;

/**
 * 数据读取类
 * Author heling on 2018/11/27
 */
public class NumReader {

    private int a;
    private int b;

    public NumReader() throws IOException {
        File file = new File("E:\\Caculator.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        a= Integer.valueOf(br.readLine());
        b= Integer.valueOf(br.readLine());
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }
}

计算类:

package com.whl.sixPrinciple;

import java.io.*;

/**
 * Author heling on 2018/11/27
 */
public class Caculator {

    public int add() throws IOException {
        NumReader numReader = new NumReader();
        return numReader.getA() + numReader.getB();
    }

    public static void main(String[] args) throws IOException {
        Caculator caculator = new Caculator();
        int result = caculator.add();
        System.out.println(result);
    }
}

由此可见,将功能分开后,我们添加其他算法的时候,就不必重复复制数据读取方法,方便维护。

二、里式替换原则

意思是一个子类应该可以替换掉父类并且可以正常工作。

那么翻译成比较容易理解的话,就是说,子类一般不该重写父类的方法,因为父类的方法一般都是对外公布的接口,是具有不可变性的,你不该将一些不该变化的东西给修改掉。

上述只是通常意义上的说法,很多情况下,我们不必太理解里氏替换,比如模板方法模式,缺省适配器,装饰器模式等一些设计模式,就完全不搭理这个原则。

不过就算如此,如果你真的遇见了不得不重写父类方法的场景,那么请你考虑,你是否真的要把这个类作为子类出现在这里,或者说这样做所换来的是否能弥补你失去的东西,比如子类无法代替父类工作,那么就意味着如果你的父类可以在某一个场景里工作的很正常,那么你的子类当然也应该可以,否则就会出现下述场景。

比如我们有某一个类,其中有一个方法,调用了某一个父类的方法。

父类:

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/27
 */
public class Parent {

    public void show() {
        System.out.println("parent method");
    }
}

子类:重写父方法

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/27
 */
public class Son extends Parent {

    @Override
    public void show() {
        System.out.println(1/0);
    }
}

某类:

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/27
 */
public class AClass {

    public void show(Parent parent) {
        parent.show();
    }

    public static void main(String[] args) {
        AClass aClass = new AClass();
        aClass.show(new Parent());
        aClass.show(new Son());
    }
}

这就相当于埋下了一个个陷阱,因为本来我们的原则是,父类可以完成的地方,我用子类替代是绝对没有问题的,但是这下反了,我每次使用一个子类替换一个父类的时候,我还要担心这个子类有没有给我埋下一个上面这种炸弹。

所以里氏替换原则是一个需要我们深刻理解的原则,因为往往有时候违反它我们可以得到很多,失去一小部分,但是有时候却会相反,所以要想做到活学活用,就要深刻理解这个原则的意义所在。      

三、接口隔离原则

也称接口最小化原则,强调的是一个接口拥有的行为应该尽可能的小。

如果违背这个原则的话,实现类必须本来不该实现的方法,比如以下案例:

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/27
 */
public interface Mobile {

    void call();

    void sendMsg();

    void map();//导航

}

比如如果某智能手机实现这个接口没问题,但是非智能手机实现的话就有问题,因为不具有导航功能,但却必须实现这个导航方法。下面是遵循接口隔离原则的设计:

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/27
 */
public interface SmartMobile extends Mobile {

    void map();
}

这样的话就不存在之前的问题了。

四、依赖倒置原则

高层模块不该依赖于低层模块,二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象。

就是说接口思想,拿前面的计算器类来举例子:

代码是从文件里读取数字,那么现在我们想要从数据库,键盘录入等其它方式获取数据,该怎么办?

也许你这样解决复制前面的NumReader,然后改掉里面获取数据的方法,那么问题来了,计算器Caculator也要去改,将NumReader改为DbReader,这就违背了依赖倒置原则,下面这样设计:

package com.whl.sixPrinciple;

/**
 * Author heling on 2018/11/28
 */
public interface Reader {

    public int getA();

    public int getB();

}

 

package com.whl.sixPrinciple;

import java.io.*;


/**
 * Author heling on 2018/11/28
 */
public class FilesReader implements Reader {

    private int a;
    private int b;

    public FilesReader() throws IOException {
        File file = new File("E:\\Caculator.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        a= Integer.valueOf(br.readLine());
        b= Integer.valueOf(br.readLine());
    }

    @Override
    public int getA() {
        return a;
    }

    @Override
    public int getB() {
        return b;
    }
}
package com.whl.sixPrinciple;

import java.io.*;

/**
 * Author heling on 2018/11/27
 */
public class Caculator {

    public int add(Reader reader) throws IOException {
//        NumReader numReader = new NumReader();
//        return numReader.getA() + numReader.getB();
//        Reader reader = new FilesReader();
        return reader.getA() + reader.getB();

    }

    public static void main(String[] args) throws IOException {
        Caculator caculator = new Caculator();
        int result = caculator.add(new FilesReader());
        System.out.println(result);
    }
}

这样就不用关心数据源哪里来,面向接口,只要得到a,b就行,不关注如何得到。

五、迪米特原则

也称最小知道原则,即一个类应该尽量不要知道其他类太多的东西,不要和陌生的类有太多接触。

这个原则的制定,是因为如果一个类知道或者说是依赖于另外一个类太多细节,这样会导致耦合度过高,应该将细节全部高内聚于类的内部,其他的类只需要知道这个类主要提供的功能即可。所谓高内聚就是尽可能将一个类的细节全部写在这个类的内部,不要漏出来给其他类知道,否则其他类就很容易会依赖于这些细节,这样类之间的耦合度就会急速上升,这样做的后果往往是一个类随便改点东西,依赖于它的类全部都要改。

六、开闭原则

对修改关闭,对扩展开放。

 

总结:

六大原则尽量遵循,不一定完全遵循,就像数据库不一定一定要按照三大范式设计,可以反范式。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值