GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

时间:2024-04-13 21:02:54

前言:

设计模式,前人总结下留给后人更好的设计程序,为我们的程序代码提供一种思想与认知,如何去更好的写出优雅的代码,23种设计模式,是时候需要掌握它了。

 

1.工厂模式

大白话:比如你需要一辆汽车,你无需了解汽车是怎么样生产的,你只需要告诉汽车厂,我要买某某某型号的汽车,????,汽车厂造好了给你即可。这就是工厂模式:

隐藏具体对象实例的过程,只需要告诉这个工厂你想要的东西(对象) 它帮你实现,你不必关系具体的实现过程;

 

举个经常用到的例子:

 

  1. 数据库连接当中指明你的数据库类型:mysql
  2. 数据库分页插件当中的方言

实践:

1、创建一个汽车工厂接口

/**
 * 汽车生产工厂
 */
public interface CarFactory {
    //创造汽车方法
    void createCar();

}

 

2、使用三个不同的类实现工厂方法

public class DazoCar implements CarFactory {

    @Override
    public void createCar() {
        System.out.println("生产大众汽车");
    }
}
public class BYDCar implements CarFactory {

    @Override
    public void createCar() {
        System.out.println("生产BYD汽车");
    }
}
public class BenciCar implements CarFactory {

    @Override
    public void createCar() {
        System.out.println("生产奔驰汽车");
    }
}

 

3、创建一个汽车工厂

public class Factory {


    public static CarFactory getCarFactroy(String type){

        //大众
        if ("DAZO".equals(type)) {
            return new DazoCar();
        } else if ("BYD".equals(type)) {
            //BYD
            return new BYDCar();
        } else if ("BENCI".equals(type)){
            //奔驰
            return new BenciCar();
        }
        return null;
    }
}

4、测试调用

    public static void main(String[] args) {

        CarFactory carFactory = Factory.getCarFactroy("BYD");

        carFactory.createCar();

    }

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 这就是一个最简单的工厂模式;

 

抽象工厂

大白话:比如小米工厂就是一个最大的抽象工厂,它里面不仅有生产手机的工厂,也有生产家用小米电器的工厂,工厂里面套工厂,这就是抽象工厂

就按照这个例子,有一家抽象工厂,它可以有生产手机和生产电器的工厂,它的工厂有具体的实现类(小米或者华为的工厂)

 

1、创建一个生产手机的工厂

 

public interface PhoneFactory {

    //生产方法
    void create();
}

 

2、创建一个生产电器的工厂

//电器工厂,生产电器
public interface DianFactory {

    //创造电器
    void createDian();

}

 

3、创建一个抽象工厂,这个抽象工厂可以同时生产电器、手机

public abstract class Factory {

    //获取手机工厂
    public abstract PhoneFactory getPhoneFactory(String type);

    //获取电器工厂
    public abstract DianFactory getDianFactory(String type);

}

 

4、具体的抽象工厂继承类 比如小米可以生产、华为也可以生产

//小米工厂
public class XiaoMiFactory extends Factory {

    @Override
    public PhoneFactory getPhoneFactory(String type) {
        return new XiaomiPhone();
    }

    @Override
    public DianFactory getDianFactory(String type) {
        return new XiaoMiDian();
    }
}

小米手机的工厂生产小米手机、小米电器

public class XiaomiPhone implements PhoneFactory {

    @Override
    public void create() {
        System.out.println("小米手机");
    }
}
public class XiaoMiDian implements DianFactory {

    @Override
    public void createDian() {
        System.out.println("小米生产电器");
    }
}

 

同理、华为也可以继承工厂类、它也可以生产手机和电器

public class HuaWeiFactory extends Factory {

    @Override
    public PhoneFactory getPhoneFactory(String type) {
        return new HuaWeiPhone();
    }

    @Override
    public DianFactory getDianFactory(String type) {
        return new HuaWeiDian();
    }
}

 

public class HuaWeiPhone implements PhoneFactory {

    @Override
    public void create() {
        System.out.println("生产华为手机");
    }
}

 

public class HuaWeiDian implements DianFactory {

    @Override
    public void createDian() {
        System.out.println("华为电器");
    }
}

 

5.创建一个工厂调度器,来取出需要的工厂

public class FactoryProducer {

    //按照名称获取工厂
    public static Factory getFactory(String name){

        if ("XIAOMI".equals(name)) {
            return new XiaoMiFactory();
        } else if ("HUAWEI".equals(name)) {
            return new HuaWeiFactory();
        }
        return null;
    }
}

 

最后我们main方法测试,传入HUAWEI, 则调出的是华为工厂,然后调用华为生产手机或者华为生产电器即可

public static void main(String[] args) {


        Factory factory = getFactory("HUAWEI");

        PhoneFactory phoneFactory = factory.getPhoneFactory(null);

        phoneFactory.create();

    }

 

单例模式

大白话:所谓单例模式,就是这个类在你的系统中只作为一个的存在,是为了防止重复的创造对象、销毁对象所带来的内存的开销。并且在这个类当中提供一个全局访问点

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

 

举个栗子:

大家都知道我们用SpringBoot作为服务端,向前台返回数据的时候,一般返回的都是JsonObject

这个Object所有的控制器返回数据都可以用到,我们这里就可以考虑用单例模式来初始化这个类,

单例模式在这里分为好几个模块;

  • 懒汉式
  • 饿汉式(最常用)在初始化类的时候就初始化成员变量
public class JsonObject {

    private static Map<String,Object> map = new HashMap<>(16);

    //构造方法私有
    private JsonObject(){}

    //提供全局访问点 synchronized(线程安全)
    public static synchronized Object resultOk(Object data){
        map.clear();

        map.put("status","ok");
        map.put("data",data);
        map.put("msg","请求成功");

        return map;
    }

    //提供全局访问点
    public static synchronized Object resultError(String msg){
        map.clear();

        map.put("status","ok");
        map.put("data",null);
        map.put("msg",msg);

        return map;
    }

}

 

我们尝试这调用

    public static void main(String[] args) {
        System.out.println(JsonObject.resultError("密码不能为空"));
        System.out.println(JsonObject.resultOk("192.168.0.1"));
    }

 

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

这样就完成了一个饿汉式(常用)的一种单例模式

 

建造者模式

大白话:将一个庞大的系统拆分成小份、小份之间互不影响、小份有者同样的制造过程,这就是建造者模式

 

举个例子:

我们去肯德基吃快餐, 肯定有它店铺的套餐可以供我们选择,套餐就是庞大的系统,套餐里面最简单的有:汉堡、饮料(组成小份),他们可以任意搭配组成不同的价格

小份有着相同的制造过程,比如汉堡用纸盒包装/饮料用瓶子包装,这里的包装就是建造过程。

 

何时使用:一些基本部件不会变,而其组合经常变化的时候。

测试代码:

//一个商品的超类接口

//单品接口
public interface Item {

    //单品名称
    String name();

    //价格
    Double price();

    //打包方式
    Packing pack();

}

//商品有各自的打包方式 创建一个打包的接口

//包装方式
public interface Packing {

    //打包方式
    String packing();

}

// 汉堡实现商品的接口 并且完善自己的打包方式;

public class Hanbao implements Item {

    @Override
    public String name() {
        return "汉堡";
    }

    @Override
    public Double price() {
        return 21.00;
    }

    @Override
    public Packing pack() {
        return new HeZhuang();
    }
}

//汉堡是盒装

public class HeZhuang implements Packing {

    @Override
    public String packing() {
        return "盒装";
    }
}

 

//可乐实现商品接口

public class Kele implements Item {

    @Override
    public String name() {
        return "可乐";
    }

    @Override
    public Double price() {
        return 5.00;
    }

    @Override
    public Packing pack() {
        return new PingZhuang();
    }
}

 

//可乐独有的打包方式

public class PingZhuang implements Packing {

    @Override
    public String packing() {
        return "瓶装";
    }
}

//建造者模式提供一个菜单组合 用于组合想要的单品组成套餐

 

public class ItemList {

    private List<Item> list = new ArrayList<>();

    //增加一个套餐
    public void addItem(Item item){
        list.add(item);
    }

    //组合总价
    public Double getPrice(){

        Double price = 0.00;

        for (Item item : list) {
            price+= item.price();
        }
        return price;
    }
    //组合详情
    public void detail() {
        for (Item item : list) {

            System.out.print(item.name()+"-->");
            System.out.print(item.price()+"-->");
            System.out.print(item.pack().packing()+"-->");
        }

    }

}

 

//开始测试

public class Main {

    public static void main(String[] args) {
        System.out.print(no1().getPrice());
        no1().detail();
        System.out.println("");
        no2().detail();
        System.out.print(no2().getPrice());


    }

    //套餐一 汉堡2 + 可乐1
    public static ItemList no1(){

        Hanbao hanbao = new Hanbao();
        Hanbao hanbao2 = new Hanbao();

        Kele kele = new Kele();

        ItemList list = new ItemList();

        list.addItem(hanbao);
        list.addItem(hanbao2);
        list.addItem(kele);

        return list;
    }


    //套餐二 汉堡1 + 可乐2
    public static ItemList no2(){

        Hanbao hanbao = new Hanbao();

        Kele kele = new Kele();
        Kele kele2 = new Kele();

        ItemList list = new ItemList();

        list.addItem(hanbao);
        list.addItem(kele2);
        list.addItem(kele);

        return list;
    }

}

 

这里用连个方法组合了两个套餐,这就是建造者模式 ,

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

原型模式(克隆对象)

大白话:用于创建重复的对象,用克隆对象的方式代替new 关键字的使用。

 

 

//对象实现Cloneable 重写父类的clone方法

public class Student implements Cloneable {

    private String id;

    private String name;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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


    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

}

 

//测试 分别用new的方式与clone的方式 创建100个相同的对象需要的毫秒数

public static void main(String[] args) {

        long start = System.currentTimeMillis();
        System.out.println(start);

        for (int i = 0;i<100;i++) {

            Student student = new Student();
            student.setId("1");
            student.setName("mrc");

            System.out.println(student.getId());
            System.out.println(student.getName());
        }

        long end = System.currentTimeMillis();
        System.out.println(end);

        System.out.println("总耗时:"+(end-start));


    }

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

//通过克隆的方式创造相同的对象进行测试

public static void main(String[] args) {

        long start = System.currentTimeMillis();
        System.out.println(start);

        Student student = new Student();
        student.setId("1");
        student.setName("mrc");

        for (int i = 0;i<100;i++) {

            Student student1 = (Student) student.clone();

            System.out.println(student1.getId());
            System.out.println(student1.getName());
        }

        long end = System.currentTimeMillis();
        System.out.println(end);

        System.out.println("总耗时:"+(end-start));


    }

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

果然性能上还是有差距的

 

适配器模式

大白话:我们现在应用写的接口是TF卡接口 现在我们要用接口对接到电脑的USB上,这时候怎么办,我们就需要一个读卡器(适配器)既有USB接口,又有TF卡接口 这就是一个适配器

 


这里我们来模拟:定义一个USB接口

public interface USBInterface {

    //读取
    void read();

    //写入
    void write();

}

//USB2.0实现接口

public class USB20 implements USBInterface {


    @Override
    public void read() {
        System.out.println("USB2.0读取");
    }

    @Override
    public void write() {
        System.out.println("USB2.0写入");
    }
}

//定义一个电脑接口

public interface Computer {

    //电脑可以读取USB
    void readUsb(USBInterface usbInterface);

}

//用惠普电脑去实现电脑

public class HpComputer implements Computer {

    @Override
    public void readUsb(USBInterface usbInterface) {
        usbInterface.read();
    }
}

##

这时候就可以测试一个USB在电脑上的读取了

    public static void main(String[] args) {


        USBInterface usb20 = new USB20();

        Computer computer = new HpComputer();

        computer.readUsb(usb20);


    }

 

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

现在需要把TF卡接入到USB接口上 但是TF接口与USB不适合 需要中间要有一个适配器

//TF卡接口
public interface TF {
    //读取TF卡
    void readTF();
    //写入TF卡
    void writeTF();
}

 

//这里需要一个闪迪作为TF卡的实现

public class SanDiskTF implements TF {
    @Override
    public void readTF() {
        System.out.println("闪迪卡读取");
    }
    @Override
    public void writeTF() {
        System.out.println("闪迪卡写入");
    }
}

 

//接入适配器,把TF卡接入到USB接口上

public class USBAdapterTF implements USBInterface {

    //将TF卡作为属性引入
    private TF tf;

    public USBAdapterTF(TF tf){
        //构造器进行桥接
        this.tf = tf;
    }

    @Override
    public void read() {
        tf.readTF();
    }

    @Override
    public void write() {
        tf.writeTF();
    }
}

//测试使用适配器让电脑读取TF卡

    public static void main(String[] args) {
        Computer computer = new HpComputer();
        TF tf = new SanDiskTF();
        //USB适配器
        USBInterface usbAdapterTF = new USBAdapterTF(tf);
        //电脑读取
        computer.readUsb(usbAdapterTF);
    }

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

桥接模式

大白话:我们一般先定义接口,再做实现类对吧,需要加一个接口定义的话,实现类必须实现这个方法,桥接模式就是这两种类型即使改变了结构也不会受到影响。

 

//定义一个画图的接口

public interface DrawImg {

    void draw();

}

 

//再用画出红色去实现这个接口

public class DrawRed implements DrawImg {

    @Override
    public void draw() {
        System.out.println("画出红色");
    }
}

 

//用一个中间抽象层代替直接实现 抽象层引用需要实现的方法即可

public abstract class Shape {

    protected DrawImg drawImg;

    protected Shape(DrawImg drawImg){
        this.drawImg = drawImg;
    }
    //抽象衔接
    public abstract void draw();

}

 

//具体的类再去继承这个抽象方法 引用父类里面接口的属性

public class Circle extends Shape {

    protected Circle(DrawImg drawImg) {
        super(drawImg);
    }

    @Override
    public void draw() {
        drawImg.draw();
    }
}

 

//实现桥接

public static void main(String[] args) {
        DrawImg drawImg = new DrawRed();
        Circle circle = new Circle(drawImg);
        circle.draw();
    }

 

过滤器模式(结构型模式)

大白话:通过特定的规则,筛选出一组对象里面符合规则的对象

 

首先创建一个实体类,用于被筛选的对象。

public class Person {

    private String name;

    private String gender;

    public Person(String name, String gender) {
        this.name = name;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

 

创建一个接口,用于不同的规则区别

//标准接口
public interface Criteria {
    //传递需要过滤的list
    List<Person> filtration(List<Person> list);
}

//男性规则,从集合中获取出所有男性

public class ManCriteria implements Criteria {

    @Override
    public List<Person> filtration(List<Person> list) {

        List<Person> result = new LinkedList<>();
        //循环遍历。拿出男性
        list.forEach(temp ->{
            if (temp.getGender().equalsIgnoreCase("MAN")) {
                result.add(temp);
            }
        });

        return result;
    }
}

女性规则,拿出女性

public class WoManCriteria implements Criteria {


    @Override
    public List<Person> filtration(List<Person> list) {

        List<Person> result = new LinkedList<>();
        //循环遍历。拿出女性
        list.forEach(temp ->{
            if (temp.getGender().equalsIgnoreCase("WOMAN")) {
                result.add(temp);
            }
        });

        return result;
    }
}

//开始过滤测试

public static void main(String[] args) {

        String arr[] = new String[]{"MAN","WOMAN"};

        List<Person> people = new ArrayList<>();

        for (int i=0;i<10;i++) {

            Person temp = new Person();

            temp.setName(i+"");
            //随机性别 0-1
            int gen = (int) Math.round(Math.random());

            temp.setGender(arr[gen]);

            people.add(temp);
        }

        Criteria manCriteria = new ManCriteria();

        List<Person> man = manCriteria.filtration(people);

        print(man);

        Criteria womanCriteria = new WoManCriteria();

        List<Person> woman = womanCriteria.filtration(people);
        print(woman);

    }


    public static void print(List<Person> people){

        people.forEach(temp -> {
            System.out.println("[name="+temp.getName()+",sex="+temp.getGender()+"]");
        });

    }

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

进行过滤完成!!

 

组合模式

理解:一个对象里面含有一个本身的List集合,并且提供了相应的修改相同组的方法

GOF 的23种JAVA常用设计模式 学习笔记 持续更新中。。。。

 

 

 


 


这里按照这个图片来说明:老板下面是财务/人事/研发这些即可看作是一个List,相应的财务组长下面有它所管理的员工,

这就是组合模式,本身含有一个自生对象的List集合

使用:表示复杂的树形图时候。

优点:调用简单/节点可以*增加

不足:例如员工对象里面包含一个员工的LIst,但这个员工对象不是接口,违反依赖倒置原则

 

public class Employee {

    //员工名称
    private String name;

    //职位
    private String dept;

    //管辖
    private List<Employee> list;


    public Employee(String name, String dept) {
        this.name = name;
        this.dept = dept;
        this.list = new ArrayList<>();
    }
    //增加一个被管理的员工
    public void add(Employee employee) {
        this.list.add(employee);
    }
    //剔除一个
    public void remove(Employee employee) {
        this.list.remove(employee);
    }
    //获取员工列表
    private List<Employee> getEmployeeList(){
        return this.list;
    }

    //geter and seter    

}

 

 //老板下面有两个主管,分别是财务和开发,开发里面管辖了两个员工

    public static void main(String[] args) {

        Employee boss = new Employee("Ms.zhang","老板");

        Employee li = new Employee("Ms.li","财务");
        Employee kai = new Employee("Ms.long","开发");
        boss.add(li);
        boss.add(kai);

        Employee mrc = new Employee("Ms.Mrc","员工");
        Employee ddl = new Employee("Ms.DDL","员工");

        li.add(mrc);
        li.add(ddl);

        print(boss);

    }

 

 

持续更新中。。。。

 

参考:

菜鸟教程:https://www.runoob.com/design-pattern/design-pattern-tutorial.html