设计模式详解之程序设计六大原则

 设计模式(Designpattern)是一套被反复使用(spring源码当中就出现了很多模式,如模板模式,代理模式,单例模式,工厂模式等)、多数人知晓的、经过分类编目的、代码设计经验的总结。还有一种说法,设计模式是可以解决特定场景的问题的一系列方法。

 设计模式可以帮助我们改善系统的设计,增强系统的健壮性、可扩展性,为以后铺平道路,但是过多的模式也会系统变的复杂。

  在学习设计模式之前,先了解程序设计六大原则。 这些原则是指导模式的规则,原则是死的,人是活的,所以并不是要完完全全遵守这些规则,否则为何数据库会有逆范式,只是在可能的情况下,请尽量遵守。

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

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Calculator {

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

    public static void main(String[] args) throws NumberFormatException, IOException {
        Calculator calculator = new Calculator();
        System.out.println("result:" + calculator.add());
    }
}

上面这个例子有明显的多职责问题,如果想算这个文件中两个数字的差,应该是COPY出来一个方法,把最后的加号改成减号。那除法,乘法,取模要COPY四次,这就造成了很多的代码重复。下面分离出来一个Reader类用来读取数据。

package com.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Reader {

    int a,b;

    public Reader(String path) throws NumberFormatException, IOException{
        BufferedReader br = new BufferedReader(new FileReader(new File(path)));
        a = Integer.valueOf(br.readLine());
        b = Integer.valueOf(br.readLine());
    }

    public int getA(){
        return a;
    }

    public int getB(){
        return b;
    }
}

package com.test;
import java.io.IOException;

public class Calculator {

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

    public static void main(String[] args) throws NumberFormatException, IOException {
        Reader reader = new Reader("E:/data.txt");
        Calculator calculator = new Calculator();
        System.out.println("result:" + calculator.add(reader.getA(),reader.getB()));
    }

}

将一个类拆成了两个类,这样以后如果有减法,乘法等等,就不用出现那么多重复代码了,单一职责原则是六大原则当中最应该遵守的原则。

里氏替换原则这个原则表达的意思是一个子类应该可以替换掉父类并且可以正常工作

子类一般不该重写父类的方法,因为父类的方法一般都是对外公布的接口,是具有不可变性的,不该将一些不该变化的东西给修改掉。上述只是通常意义上的说法,很多情况下,不必一直遵守该原则,比如模板方法模式,缺省适配器,装饰器模式等。不过就算如此,如果真的遇见了不得不重写父类方法的场景,那么请考虑,这样做所换来的是否能弥补失去的东西,比如子类无法代替父类工作,就会出现问题。

//某一个类
public class SomeoneClass {
    //有某一个方法,使用了一个父类类型
    public void someoneMethod(Parent parent){
        parent.method();
    }
}

//父类public class Parent {

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

//子类public class SubClass extends Parent{

    //结果某一个子类重写了父类的方法,说不支持该操作了
    public void method() {
        throw new UnsupportedOperationException();
    }

}

public class Client {

    public static void main(String[] args) {
        SomeoneClass someoneClass = new SomeoneClass();
        someoneClass.someoneMethod(new Parent());
        someoneClass.someoneMethod(new SubClass());
    }
}

这就相当于埋下了一个个陷阱,因为本来父类可以完成的地方,子类替代是没有问题的,但是现在每次使用一个子类替换一个父类的时候,还要担心这个子类有没有埋下一个上面这种炸弹。里氏替换原则需要深刻理解,因为往往有时候违反它可以得到很多,失去一小部分,但是有时候却会相反,所以要想做到活学活用。

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

如果不遵守会发现这样的状况,一个类实现了一个接口,里面很多方法都是空着的,只有个别几个方法实现了。这样做不仅会强制实现的人不得不实现本来不该实现的方法,最严重的是会给使用者造成假象,即这个实现类拥有接口中所有的行为,结果调用方法时却没收获到想要的结果。比如设计一个手机的接口时,就要手机哪些行为是必须的,要让这个接口尽量的小。

public interface Mobile {

    public void call();//手机可以打电话

    public void sendMessage();//手机可以发短信

    public void playBird();//手机可以玩愤怒的小鸟?

}

上面第三个行为明显就不是一个手机必须有的,非智能手机去实现这个接口,那么playBird方法就只能空着了,建立下面这个接口去扩展现有的Mobile接口。

public interface SmartPhone extends Mobile{

    public void playBird();//智能手机的接口就可以加入这个方法了

}

这样两个接口就都是最小化的了,最小接口原则一般是要尽量满足的,如果实在有多余的方法,也有补救的办法,而且有的时候也确实不可避免的有一些实现类无法全部实现接口中的方法,这时候就轮到缺省适配器上场了,这个后面有介绍。

依赖倒置原则这个原则描述的是高层模块不该依赖于低层模块,二者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象

实现都是易变的,而只有抽象是稳定的,所以当依赖于抽象时,实现的变化并不会影响客户端的调用。比如上述的计算器例子,计算器其实是依赖于数据读取类的,这样做并不是很好,因为如果数据不是文件里的了,而是在数据库里,这样的话,为了不影响你现有的代码,你就只能将你的Reader类整个改头换面。或者还有一种方式就是,再添加一个DBReader类,然后把所有使用Reader读取的地方,全部手动替换成DBReader,这样其实也还可以接受,那假设有的从文件读取,有的从数据库读取,有的从XML文件读取等等就不好办。所以最好的做法就是抽象出一个抽象类或者是接口,来表述数据读取的行为,然后让上面所有的读取方式所实现的类都实现这个接口,而客户端只使用定义好的接口,当实现变化时,只需要设置不同的实际类型就可以了,这样对于系统的扩展性是一个大大的提升。

针对上面简单的数据读取,我们可以定义如下接口去描述。

public interface Reader {

    public int getA();

    public int getB();
}

让原来的Reader改名为FileReader去实现这个接口,这样计算器就依赖于抽象的接口,这个依赖是非常稳定的,因为不论以后要从哪读取数据,两个获取数据的方法永远都不会变。这便是依赖于抽象所得到的灵活性,这也是JAVA语言的动态特性带来的便利。

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

这个原则的制定,是因为如果一个类知道或者说是依赖于另外一个类太多细节,这样做的后果往往是一个类随便改点东西,依赖于它的类全部都要改,会导致耦合度过高,应该将细节全部高内聚于类的内部,其他的类只需要知道这个类主要提供的功能即可。

比如把上述的例子改变一下。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Reader {

    int a,b;

    private String path;

    private BufferedReader br;

    public Reader(String path){
        this.path = path;
    }

    public void setBufferedReader() throws FileNotFoundException{
        br = new BufferedReader(new FileReader(new File(path)));
    }

    public void readLine() throws NumberFormatException, IOException{
        a = Integer.valueOf(br.readLine());
        b = Integer.valueOf(br.readLine());
    }

    public int getA(){
        return a;
    }

    public int getB(){
        return b;
    }
}

Reader类改成上述这个样子,显然它给其他的类透漏了太多细节,让别人知道了它的太多细节,这样客户端调用的时候就很可能写成如下形式。

public class Client {

    public static void main(String[] args) throws Exception {
        Reader reader = new Reader("E:/test.txt");
        reader.setBufferedReader();
        reader.readLine();
        int a = reader.getA();
        int b = reader.getB();
        //以下用于计算等等
    }
}

这样客户端就依赖于reader的多个行为才能最终获取到A和B两个数值,这时候两个类的耦合度就太高了,更好的做法使用访问权限限制将二者都给隐藏起来。

public class Reader {

    int a,b;
    private String path;
    private BufferedReader br;
    public Reader(String path) throws Exception{
        super();
        this.path = path;
        setBufferedReader();
        readLine();
    }
    //注意,我们变为私有的方法
    private void setBufferedReader() throws FileNotFoundException{
        br = new BufferedReader(new FileReader(path));
    }
    //注意,我们变为私有的方法
    private void readLine() throws NumberFormatException, IOException{
        a = Integer.valueOf(br.readLine());
        b = Integer.valueOf(br.readLine());
    }

    public int getA(){
        return a;
    }

    public int getB(){
        return b;
    }
}

迪米特原则虽说是指的一个类应当尽量不要知道其他类太多细节,但其实更重要的是一个类应当不要让外部的类知道自己太多。两者是相辅相成的,只要将类的封装性做的很好,那么外部的类就无法依赖当中的细节。

开-闭原则最后一个原则,一句话,对修改关闭,对扩展开放

就是说任何的改变都不需要修改原有的代码,而只需要加入一些新的实现,就可以达到目的,这是系统设计的理想境界,但是没有任何一个系统可以做到这一点,哪怕一直最欣赏的spring框架也做不到,虽说它的扩展性已经强到变态。这个原则更像是前五个原则的总纲,前五个原则就是围着它转的,只要尽量的遵守前五个原则,那么设计出来的系统应该就比较符合开闭原则了,相反,如果违背了太多,那么系统或许也不太遵循开闭原则。

在《大话设计模式》一书中,提到一句话与各位共勉,即用抽象构建框架,用细节实现扩展。以上六个原则写出来是为了指导后面设计模式的描述,下期预告:单例模式。

      原文链接:http://www.cnblogs.com/zuoxiaolong/p/pattern1.html

原文地址:https://www.cnblogs.com/fswhq/p/Designpattern.html

时间: 2024-11-24 07:37:47

设计模式详解之程序设计六大原则的相关文章

编程常用设计模式详解--(上篇)(工厂、单例、建造者、原型)

参考来自:http://zz563143188.iteye.com/blog/1847029 一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 二.设计模式的六大原则 1

JAVA:23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代 码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用 设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我

编程常用设计模式详解--(中篇)(适配器、装饰、代理、外观、桥接、组合、享元)

摘自:http://blog.csdn.net/zhangerqing/article/details/8239539 我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式.装饰模式.代理模式.外观模式.桥接模式.组合模式.享元模式.其中对象的适配器模式是各种模式的起源,我们看下面的图: 6.适配器模式(Adapter) 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题.主要分为三类:类的适配器

设计模式详解(总纲)

转载:http://www.cnblogs.com/zuoxiaolong/p/pattern1.html <简介> 说到设计模式,当初第一次听到时,第一反应就是很深奥,完全理解不了这个概念到底是什么意思,下面我先从网上摘录一份定义. 设计模式(Designpattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 上面是百度当中的解释,来解释一下这句简单的话的含义,几个关键词.  反复使用:这个不用过多解释,设计模式被使用太多了,上个系列spring源码当中就出现了

JAVA:23种设计模式详解(转)2

我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式.装饰模式.代理模式.外观模式.桥接模式.组合模式.享元模式.其中对象的适配器模式是各种模式的起源,我们看下面的图: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题.主要分为三类:类的适配器模式.对象的适配器模式.接口的适配器模式.首先,我们来看看类的适配器模式,先看类图: 核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时

iOS中MVC等设计模式详解

iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计模式是被大家广为熟悉和使用的模式,实际上在移动开发中尤其ios开发中,这种模式被发挥到淋漓尽致 MVC设计模式包括三个部分:模型.视图和控制器. 模型包含数据.信息,逻辑,或对象被认为是部分的业务层的iOS应用. 视图包含所有的用户信息的组件,如文本区域,按钮,滑块,被认为是表示层的一个iOS应用.

初识设计模式、软件设计的六大原则

总结:本篇文字分为两个部分.第一部分:设计模式基本常识:第二部分:软件设计中的六大原则,并详细分析了单一职责原则.(本篇文章的时间轴参考:为知笔记支撑文件夹\Java设计模式(时间序列图).vsdx) 部分一:初识设计模式 什么是设计模式?James拿到这个论点时,很是迷惑! 模式?是不是一个模子?模式识别--计算机领域的经典问题? 设计模拟?软件的设计模式?不懂!!! 但是在实际编码.调试过程中,James的遇到过很是难解的问题:工程代码中有过多的冗余代码--代码复用性不高:需求一旦改变,需要

Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

0. Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周