设计模式之7大设计原则-Java版

发布时间:2024年01月19日

软件设计模式是前辈们代码设计经验的总结,可以反复使用。设计模式共分为3大类,创建者模式(5种)、结构型模式(7种)、行为型模式(11种),一共23种设计模式,软件设计一般需要满足7大基本原则。下面通过5章的学习一起来看看设计模式的魅力吧。

目录

1.1、设计模式概述

1.2、设计模式的分类

1.3、UML类图

1.4、设计原则

1.4.1 开闭原则

1.4.2、里式代换原则

1.4.3、依赖倒转原则

1.4.4、接口隔离原则

1.4.5、迪米特法则

1.4.6、合成复用原则

1.4.7、单一职责原则


1.1、设计模式概述

软件设计模式是前辈们代码设计经验的总结,本质上是对面向对象设计原则的实际应用,是对类的封装、继承、多态以及类的关联关系与组合关系的充分理解。

使用设计模式的代码可重用性与可维护性高、可读性强、可以减少软件开发周期,提升开发效率。

1.2、设计模式的分类

创建型模式(5种):本质上就是将对象的创建与使用分离,就是描述怎样去创建对象。

包括:单例、原型、工厂方法、抽象工厂、建造者模式

结构型模式(7种):本质上是将类或者对象按照某种布局组成更大的结构。

包括:代理、适配器、桥接、装饰、外观、享元、组合模式

行为模式(11种):本质是描述类与对象协助完成单个对象无法完成的任务,以及怎么分配职责。

包括:模板方法、策略、命令、责任链、状态、观察者、中介者模式、迭代器、访问者、备忘录、解释器。

1.3、UML类图

类图是面向对象建模的主要组成部门,类图显示了模型的内部结构,主要包括:模型中存在的类、类的属性以及类与类之间的关系等。

1.4、设计原则

设计模式的7大原则,具体如下:

1.开闭原则:软件实体 (类、模块、函数等等) 应该是可以被扩展的,但是不可被修改。

2.里式代换原则:继承的时候尽量不要重写父类的方法,如果重写要保证不破坏原方法的功能。

3.依赖倒转原则:抽象不应该依赖于细节,细节应该依赖于抽象,依赖接口,面向接口编程。

4.接口隔离原则:客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上。

5.迪米特法则:最少知道原则,它表示一个对象应该对其它对象保持最少的了解。通俗来说就是,只与直接的朋友通信。

6.合成复用原则:通过组合或者聚合的方式替代继承,增强封装性与提升复用的灵活性。

7.单一职责:表示一个模块的组成元素之间的功能相关性,即一个类只负责一项职责。

1.4.1 开闭原则

1.开闭原则:对扩展开放,对修改关闭。扩展源程序的时候,不要修改原有代码,通过使用接口与抽象类实现。

通过定义一个抽象类,抽象类中定义抽象方法,通过多个子类继承抽象类并重写重写抽象方法,同时设置一个中间类用于注入抽象类并调用抽象方法,这样在客户端测试类中就可以实例化中间类,并注入抽象类的子类,即实现了扩展类方法,也没更改代码本身。

1.定义一个抽象类,如下:

/**
 * @ClassName AbstractSkin
 * @description: 抽象皮肤类
 * @author nuist__NJUPT
 * @date 2024年01月18日
 * @version: 1.0
 */
public abstract class AbstractSkin {
    // 显示方法
    public abstract void display() ;


}

2.定义两个子类,继承抽象类并实现抽象方法display


/**
 * @ClassName DefaultSkin
 * @description: 默认皮肤
 * @author nuist__NJUPT
 * @date 2024年01月18日
 * @version: 1.0
 */
public class DefaultSkin extends AbstractSkin{

    @Override
    public void display() {
        System.out.println("默认皮肤");
    }

}


/**
 * @author nuist__NJUPT
 * @ClassName NewSkin
 * @description: 新皮肤
 * @date 2024年01月18日
 * @version: 1.0
 */
public class NewSkin extends AbstractSkin {

    @Override
    public void display() {
        System.out.println("新皮肤");
    }
}

3.定义一个中间类,在中间类中注入抽象类,并调用抽象方法。

/**
 * @author nuist__NJUPT
 * @ClassName SogouInput
 * @description: 搜狗输入法
 * @date 2024年01月18日
 * @version: 1.0
 */
public class SogouInput {

    private AbstractSkin abstractSkin ;

    public void setAbstractSkin(AbstractSkin abstractSkin) {
        this.abstractSkin = abstractSkin;
    }

    public void display(){
        abstractSkin.display();
    }
}

4.定义一个客户端测试类,在测试类中实例化中间类,并注入实例化子类,即可调用子类方法

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: TODO
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Client {
    public static void main(String[] args) {
        //1.创建搜狗输入法对象
        SogouInput sogouInput = new SogouInput() ;
        //2.创建皮肤对象并将皮肤设置到输入法
        sogouInput.setAbstractSkin(new DefaultSkin());
        // sogouInput.setAbstractSkin(new NewSkin());
        //4.显示皮肤
        sogouInput.display();
    }

}
1.4.2、里式代换原则

里式代换原则:子类可以扩展父类的功能,但是不允许更改父类的内容。

子类继承父类得时候,可以新增方法,但是尽量不要重写父类当中得方法

这里通过定义接口,在接口中定义方法,通过实现类的方式实现接口中方法的功能。

1.定义四边形接口,并定义获取长于宽的方法。


/**
 * @author nuist__NJUPT
 * @InterfaceName Quadrilateral
 * @description: 四边形接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface Quadrilateral {

    double getLength() ;

    double getWidth() ;

}

2.定义四边形接口的两个实现类,正方法与长方形。



/**
 * @author nuist__NJUPT
 * @ClassName Rectangle
 * @description: 长方形实现类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Rectangle implements Quadrilateral {

    private double length ;
    private  double width ;

    public void setLength(double length) {
        this.length = length;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    @Override
    public double getLength() {
        return length;
    }

    @Override
    public double getWidth() {
        return width;
    }
}


/**
 * @author nuist__NJUPT
 * @ClassName Square
 * @description: 正方形实现类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Square implements Quadrilateral {

    private double side ;

    public double getSide() {
        return side;
    }

    public void setSide(double side) {
        this.side = side;
    }

    @Override
    public double getLength() {
        return side;
    }

    @Override
    public double getWidth() {
        return side;
    }
}

3.定义测试类,测试通过实现类进行扩充长宽并打印。

import com.company.p1.demo2.Rectangle;

/**
 * @author nuist__NJUPT
 * @ClassName RectangleDemo
 * @description: 测试类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class RectangleDemo {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle() ;
        rectangle.setLength(20);
        rectangle.setWidth(10);
        resize(rectangle);
        print(rectangle);
    }
    /**
     * 扩宽方法
     * @param rectangle
     */
    public static void resize(com.company.p1.demo2.Rectangle rectangle){
        // 宽比长小则扩充
        while(rectangle.getLength() >= rectangle.getWidth()){
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    /**
     * 打印长与宽
     * @param rectangle
     */
    public static void print(Rectangle rectangle){
        System.out.println("长方形的长为:" + rectangle.getLength());
        System.out.println("长方形的宽为:" + rectangle.getWidth());
    }

}
1.4.3、依赖倒转原则

依赖倒转原则:模块应该依赖于抽象,就是面向抽象编程,而不是对实现进行编程,这样可以降低客户与实现之间的耦合。具体说就是依赖接口,而不是依赖具体的实现类。

首先定义三个接口,硬盘,内存,cpu的如下:


/**
 * @author nuist__NJUPT
 * @InterfaceName HardDisk
 * @description: 硬盘接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface HardDisk {

    // 存储数据
    public void save(String data) ;

    // 获取数据
    public String get() ;
}
/**
 * @author nuist__NJUPT
 * @InterfaceName Cpu
 * @description: CPU接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface Cpu {

    public void run() ;

}

/**
 * @author nuist__NJUPT
 * @InterfaceName Memory
 * @description: 内存条接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface Memory {
    //存储数据的接口
    public void save() ;

}

然后分别定义三个接口的实现类,如下:


/**
 * @author nuist__NJUPT
 * @ClassName XiJieHardDisk
 * @description: 硬盘实现类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class XiJieHardDisk implements HardDisk {

    @Override
    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据");
    }

    @Override
    public String get() {
        return "从硬盘中获取的数据" ;
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName IntelCpu
 * @description: Intel的CPU
 * @date 2024年01月18日
 * @version: 1.0
 */
public class IntelCpu implements Cpu {

    @Override
    public void run() {
        System.out.println("运行Intel的cpu");
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName KingstonMemory
 * @description: TODO
 * @date 2024年01月18日
 * @version: 1.0
 */
public class KingstonMemory implements Memory {

    @Override
    public void save() {
        System.out.println("使用金士顿内存条");
    }
}

然后定义一个中间类,用于依赖这些接口,而不是这些实现类。

/**
 * @author nuist__NJUPT
 * @ClassName Computer
 * @description: 计算机类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Computer {
    /**
     * 依赖的是抽象接口,而不是具体的实现类,这样后面增加新的实现类不需要改动Computer
     * 只需要增加实现类,并且在测试类中进行实例化就可以了
     */
    private HardDisk hardDisk ;
    private Cpu cpu ;
    private Memory memory ;

    public HardDisk getHardDisk() {
        return hardDisk;
    }

    public void setHardDisk(HardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public Cpu getCpu() {
        return cpu;
    }

    public void setCpu(Cpu cpu) {
        this.cpu = cpu;
    }

    public Memory getMemory() {
        return memory;
    }

    public void setMemory(Memory memory) {
        this.memory = memory;
    }

    /**
     * 运行计算机的方法
     */
    public void run(){
        System.out.println("运行计算机");
        System.out.println("从硬盘上获取的数据:" + hardDisk.get());
        cpu.run();
        memory.save();
    }
}

最后定义测试类,在测试类中实例化中间类与实现类,并调用实现类的方法,在测试类中依赖具体,而不是在中间类,中间类依赖的是抽象的接口。这样在就可以避免中间层的代码修改,只需要增加实现类然后在测试类,也就是客户端修改代码即可。

/**
 * @author nuist__NJUPT
 * @ClassName ComputerDemo
 * @description: 测试类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class ComputerDemo {
    public static void main(String[] args) {


        HardDisk hardDisk = new XiJieHardDisk() ;
        Cpu cpu = new IntelCpu() ;
        Memory memory = new KingstonMemory() ;

        Computer computer = new Computer() ;
        computer.setCpu(cpu);
        computer.setMemory(memory);
        computer.setHardDisk(hardDisk);

        computer.run();


    }

}
1.4.4、接口隔离原则

接口隔离原则:一个类对另外一个类的依赖应该建立在最小的接口上,也就是定义接口的功能最小化,然后通过实现类的方式重写接口中的抽象方法即可,这样可以避免客户端依赖的方法是它并不使用的。

1.定义三个相互隔离的最小化接口,而不是定义一个接口包含三个方法,避免实现类依赖不使用的方法。


/**
 * @author nuist__NJUPT
 * @InterfaceName AntiTheft
 * @description: 防盗接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface AntiTheft {

    void antiTheft() ;

}
/**
 * @author nuist__NJUPT
 * @InterfaceName Fireproof
 * @description: 防火接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface Fireproof {

    void fireproof() ;
}
/**
 * @author nuist__NJUPT
 * @InterfaceName Waterproof
 * @description: 防水接口
 * @date 2024年01月18日
 * @version: 1.0
 */
public interface Waterproof {
    void waterproof() ;
}

2.定义多个实现类,分别实现接口并重写接口中的抽象方法,需要用到接口方法的,才去实现接口。

/**
 * @author nuist__NJUPT
 * @ClassName SafeDoor
 * @description: TODO
 * @date 2024年01月18日
 * @version: 1.0
 */
public class SafeDoor implements AntiTheft, Fireproof, Waterproof {

    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireproof() {
        System.out.println("防火");
    }

    @Override
    public void waterproof() {
        System.out.println("防水");
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName SafeDoor2
 * @description: TODO
 * @date 2024年01月18日
 * @version: 1.0
 */
public class SafeDoor2 implements AntiTheft, Fireproof{

    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireproof() {
        System.out.println("防火");
    }
}

3.在测试类中实例化实现类,并调用相应的实现方法。


/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description:测试类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Client {
    public static void main(String[] args) {
        // 实例化对象
        SafeDoor safeDoor = new SafeDoor() ;
        safeDoor.antiTheft();
        safeDoor.fireproof();
        safeDoor.waterproof();

        System.out.println("-----------------------------");

        SafeDoor2 safeDoor2 = new SafeDoor2() ;
        safeDoor2.antiTheft();
        safeDoor2.fireproof();
    }
}
1.4.5、迪米特法则

迪米特法则:最少只知识原则,也就是说如果两个实体无需直接通信,就尽量不要产生直接交互,这样可以降低模块的耦合度,提高模块的独立性。

1、首先定义三个类,分别为公司类、明星类、粉丝类

/**
 * @author nuist__NJUPT
 * @ClassName Company
 * @description: 公司类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Company {
    private String name ;
    public Company(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName Star
 * @description: 明星类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Star {

    private String name ;

    public Star(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName Fans
 * @description: 粉丝类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Fans {
    private String name ;
    public Fans(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

2、定义一个经纪人的类,通过该类实现明星与粉丝,以及明星与公司的交互,避免它们直接交互,降低模块的耦合性,提高模块的独立性。

/**
 * @author nuist__NJUPT
 * @ClassName Agent
 * @description: 经纪人类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Agent {
    private Star star ;
    private Company company ;
    private Fans fans ;

    public Star getStar() {
        return star;
    }

    public void setStar(Star star) {
        this.star = star;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public Fans getFans() {
        return fans;
    }

    public void setFans(Fans fans) {
        this.fans = fans;
    }

    /**
     * 粉丝见面方法
     */
     public void meeting(){
         System.out.println("明星:" + star.getName() + "与粉丝:" + fans.getName()+"见面");
     }
    /**
     * 与公司洽谈
     */
    public void business(){
        System.out.println("明星:" + star.getName() + "与公司:" + company.getName()+"洽谈");
    }

}

3.最后在测试类实例化经纪人类,并调用相应的方法。

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 测试类
 * @date 2024年01月18日
 * @version: 1.0
 */
public class Client {
    public static void main(String[] args) {
        Agent agent = new Agent() ;
        agent.setStar(new Star("刘德华"));
        agent.setCompany(new Company("大碗娱乐公司"));
        agent.setFans(new Fans("张三"));

        agent.meeting();
        agent.business();
    }
}
1.4.6、合成复用原则

合成复用原则:尽量使用组合或者聚合等关联关系来实习复用,而不是通过继承的方式实现复用

因为继承首先破环了类的封装性,父类暴露给子类了,同时子类与父类的耦合度高,另外继承限制了复用的灵活性。

1.4.7、单一职责原则

单一职责原则,通俗的来说就是尽量让一个类中只负责一件事,当然这个还是要结合实际情况来看。有些场景可以一个类中做多件事,然后子类继承并重写方法。

我们假设有两种动物,分别呼吸空气与水,那么可以定义两个类,每个类中定义一个方法。这样满足单一职责原则。

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 单一职责测试类
 * @date 2024年01月19日
 * @version: 1.0
 */
class Terrestrial{
    public void breathe(String animal){
        System.out.println(animal + "呼吸空气");
    }
}
class Aquatic{
    public void breathe(String animal){
        System.out.println(animal + "呼吸水");
    }
}

public class Client{
    public static void main(String[] args){
        Terrestrial terrestrial = new Terrestrial();
        terrestrial.breathe("牛");
        terrestrial.breathe("羊");
        terrestrial.breathe("猪");

        Aquatic aquatic = new Aquatic();
        aquatic.breathe("鱼");
    }
}  
文章来源:https://blog.csdn.net/nuist_NJUPT/article/details/135660264
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。