设计模式-七大原则

Posted excellencesy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式-七大原则相关的知识,希望对你有一定的参考价值。

软件开发七大原则

一、开闭原则:面对扩展开放,面对修改关闭

(1)指的是在开发的过程中尽量的去扩展代码,而不是去修改原来的代码,以免影响到之前的逻辑。

(2)强调的是用抽象构建框架,用实现扩展细节。

(3)可以提高软件系统的可复用性及可维护性

(2)例:原有课程类,闲杂要对课程进行打折,应该怎么处理呢?
原有课程类:

package test1;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public interface Course {
    public String getId();
    public String getName();
    public double getPrice();
}

 

package test1;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class JavaCourse implements Course {
  private String id;
  private String name;
  private double price;

    @Override
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public JavaCourse(String id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
}

 

 新增打折课程类

package test1;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class JavaDiscountCourse extends JavaCourse{

    public JavaDiscountCourse(String id, String name, double price) {
        super(id, name, price);
    }

    public double getOriginPrice() {
        return super.getPrice();
    }

    public double getPrice() {
        return super.getPrice()*0.8;
    }

}

 

 

在不修改原来的逻辑的基础上作出对应的修改

测试类:

package test1;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Test {
    public static void main(String[] args) {
        JavaDiscountCourse cource = new JavaDiscountCourse("001","java",100.0);
        System.out.println(cource.getPrice());
        System.out.println(cource.getOriginPrice());
    }
}

 

 二、依赖倒置原则:依赖于抽象接口,不要依赖于具体实现。

(1)要求对抽象进行编程,不要对实现进行编程。

(2)降低了客户与实现模块间的耦合。

(3)例:原有转换工具类可以转换word,pdf两种类型的文件,现在该工具类需要扩展使其在原来的基础上还能转换excel文件

如果是按照依赖实现编程:

package test2;

/**
 * des: office文档转pdf类
 * author:songyan
 * date: 2019/10/6
 **/
public class Trans {
    public void transWord(){
        System.out.println("转换");
    }
    public void transPDF(){
        System.out.println("pdf转换");
    }
}

 

 

客户端:

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Client {
    public static void main(String[] args) {
        Trans trans = new Trans();
        trans.transWord();
        trans.transPDF();
    }
}

 

使用这种方式的弊端就在于,如果说要扩展转换工具类的工具类的功能,就需要去修改之前的代码,显然这种做法是非常不安全的,有可能就会影响之前代码。

更好的做法是:

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class TransUtil {
    public void trans(OfficeDocument document){
        document.trans();
    }
}

 

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class WordDocument implements OfficeDocument {
    @Override
    public void trans() {
        System.out.println("word文档转换");
    }
}

 

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class PDFDocuemnt implements OfficeDocument{

    @Override
    public void trans() {
        System.out.println("pdf转换");
    }
}

 

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Test {
    public static void main(String[] args) {
        TransUtil trnas = new TransUtil();
        trnas.trans(new WordDocument());
        trnas.trans(new PDFDocuemnt());

    }
}

 

在转换工具类中是针对处理对象的接口进行处理的,在想要扩展功能的时候只需要添加一个实现类即可,例如:

package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class ExcelDocument implements OfficeDocument{

    @Override
    public void trans() {
        System.out.println("excel转换");
    }
}
package test2;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Test {
    public static void main(String[] args) {
        TransUtil trnas = new TransUtil();
        trnas.trans(new WordDocument());
        trnas.trans(new PDFDocuemnt());
        trnas.trans(new ExcelDocument());

    }
}

 

使用这种方法只需要扩展之前的代码,而不需要修改之前的代码,其实也就是上面说开闭原则。

这个例子其实就是“依赖注入”,那么依赖注入的方式又包括构造器注入,setter方法注入,下面简介一下这两种方式

1)构造器注入

package test2.generator;

import test2.OfficeDocument;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class TransUtil {
    private OfficeDocument officeDocument;

    public TransUtil(OfficeDocument officeDocument) {
        this.officeDocument = officeDocument;
    }

    public void trans() {
        officeDocument.trans();
    }
}
package test2.generator;

import test2.WordDocument;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Test {
    public static void main(String[] args) {
        TransUtil transUtil = new TransUtil(new WordDocument());
        transUtil.trans();
    }
}

 

2)setter方式注入

package test2.setter;

import test2.OfficeDocument;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class TransUtil {
    private OfficeDocument officeDocument;

    public void setOfficeDocument(OfficeDocument officeDocument) {
        this.officeDocument = officeDocument;
    }

    public void trans(){
        officeDocument.trans();
    }
}
package test2.setter;

import test2.WordDocument;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class Test {
    public static void main(String[] args) {
        TransUtil  transUtil= new TransUtil();
        transUtil.setOfficeDocument(new WordDocument());
        transUtil.trans();
    }
}

 

以抽象为基准比以细节为基准搭建起来的框架要稳健的多,因此,大家在拿到需求之后要面向抽象接口来编程,先顶层在底层来设计代码结构。

三、单一职责原则:一个类,一个接口,一个方法应该只有一个职能

(1)如果有多个职能,则其中一个职能发生改变之后,就需要修改这个类的功能,就有可能影响到另一个职能。所以我们有必要对他们进行一定程度的拆分。

(2)降低类的复杂度,提高类的可读性,提高系统的可维护性,降低变更引起的风险

(3)例:有一个对nginx操作的工具类如下:

package test3;

/**
 * Nginx工具类
 * author:songyan
 * date: 2019/10/6
 **/
public class NginxUtil {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void start(){
        System.out.println("启动nginx");
    }
}

 

里面有设计nginx基本信息的方法,也有启动nginx的方法,比如说,nginx的启动方法发生了改变,这个时候就需要修改nginxUtil类,那再修改的过程中就有可能对他的其他方法产生影响,因此我们可以对他进行以下划分,将他隔离开:

package test3;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class NginxInfo {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package test3;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class NginxOperator {
    public void start(){
        System.out.println("nginx启动");
    }
}

 

接口与此类似。。

方法不符合单一职责而原则的例子:

    public void modefied(String name,String fav){
        System.out.println("修改名字");
        System.out.println("修改爱好");
    }

 

方法符合单一职责而原则的例子:

    public void modefiedName(String name){
        System.out.println("修改姓名");
    }
    public void modefiedFav(String fav){
        System.out.println("修改爱好");
    }

 

其实这种方式乍一看可能感觉不到这样写的好处在哪里,我突然想到我最近的一个任务,是修改之前的同事写的一个系统,里面大多数的逻辑代码都是摞在一起的,一个方法中有几百行代码,做什么的都有,可能有的时候只需要改其中的一个点,但是可能就会影响到其他部分的代码,还有一个缺点就是,里面的代码错综复杂,可能的要找到你想改的地方都很难。

总结一下就是,在写代码的过程中如果尽量的保持单一指责原则就会,提高类的可读性,提高系统的可维护性,降低变更引起的风险。

四、接口隔离原则:使用多个专一的接口,而不使用单一的总接口

(1)一个类对一个类的接口应该建立在最小的接口之上。

(2)尽力单一的接口,而不要建立单一臃肿的接口

(3)尽量的细化接口,接口中的方法尽量少(不是越少越好,要适度)

(4)例:前几天总结的office文档转pdf的方式,其中openoffice,aspose是支持windows,linux两种系统的,但是jacob只支持windows系统,下面的代码就会存在一定的问题。

在下面的代码中,将windows转换的方式,Linux转换的方式放在了一个接口中:

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public interface Itrans {
    void windowsTrans();
    void linuxTrans();

}

 

在openoffice,aspose两种方式中,是没有问题的,见下:

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class AsposeTrans implements  Itrans{
    @Override
    public void windowsTrans() {
        System.out.println("Aspose在windows的转换");

    }

    @Override
    public void linuxTrans() {
        System.out.println("Aspose在linux的转换");

    }
}
package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class OpenofficeTrans implements Itrans{
    @Override
    public void windowsTrans() {
        System.out.println("oppenoffice在windows的转换");
    }

    @Override
    public void linuxTrans() {
        System.out.println("oppenoffice在linux的转换");
    }
}

 

但是,你会发现在jacob中,他是不支持在Linux的转换的,但是实现这个接口的话必须重写这个接口。。

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class JacobTrans implements Itrans{
    @Override
    public void windowsTrans() {
        System.out.println("Jacob在windows的转换");
    }

    @Override
    public void linuxTrans() {

    }
}

 

针对上面的情况可以做以下完善,将接口中的方法拆分到两个接口中:

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public interface IWindowsTrans {
    void windowsTrans();
}
package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public interface ILinuxTrans {
    void linuxTrans();
}

 

对两种系统都支持的方式可以去实现两种接口

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class AsposeTrans implements  IWindowsTrans,ILinuxTrans{
    @Override
    public void windowsTrans() {
        System.out.println("Aspose在windows的转换");

    }

    @Override
    public void linuxTrans() {
        System.out.println("Aspose在linux的转换");

    }
}

 

对只支持一种系统的方式只需要实现一种接口

package test4;

/**
 * author:songyan
 * date: 2019/10/6
 **/
public class JacobTrans implements  IWindowsTrans{
    @Override
    public void windowsTrans() {
        System.out.println("Jacob在windows的转换");
    }
    
}

 

以上是关于设计模式-七大原则的主要内容,如果未能解决你的问题,请参考以下文章

设计模式软件设计七大原则 ( 依赖倒置原则 | 代码示例 )

解密设计模式的七大原则

设计模式软件设计七大原则 ( 迪米特原则 | 代码示例 )

设计模式软件设计七大原则 ( 接口隔离原则 | 代码示例 )

设计模式七大原则开闭原则

Python设计模式 - 基础 - 七大基本原则