设计模式(1)创建型模式和结构型模式

1、目标

本文的主要目标是学习创建型模式和结构型模式,并分别代码实现每种设计模式

2、创建型模式

2.1 单例模式(singleton)

单例模式是创建一个对象保证只有这个类的唯一实例,单例模式分为饿汉式和懒汉式,饿汉式是类加载的时候就创建这个类的唯一对象,懒汉式是获取这个类的对象时才会创建对象

程序:

public class SingletonFactory {// 饿汉式的单例模式public static class Singleton01 {private static final Singleton01 INSTANCE = new Singleton01();// 构造器私有化private Singleton01() {}public static Singleton01 getInstance() {return INSTANCE;}}// 懒汉式的单例模式(不加锁,推荐)public static class Singleton02 {// 使用静态内部类封装单例对象,实现懒加载,也可以保证线程安全private static class Singleton02Holder {private static final Singleton02 INSTANCE = new Singleton02();}// 构造器私有化private Singleton02() {}public static Singleton02 getInstance() {return Singleton02Holder.INSTANCE;}}// 懒汉式的单例模式(加锁,双端检查)public static class Singleton03 {// 不会初始化INSTANCE,必须加上volatile保证可见性和禁止指令重排序// volatile的作用:// 1、保证可见性:修改INSTANCE变量会立即更新到主内存中,其他线程读取INSTANCE变量会从主内存中读取数据,保证实例的唯一性// 2、禁止指令重排序:必须先初始化INSTANCE对象然后对象空间赋值给INSTANCE变量,保证实例的完整性private static volatile Singleton03 INSTANCE;// 构造器私有化private Singleton03() {}public static Singleton03 getInstance() {if(INSTANCE != null) {return INSTANCE;}synchronized (Singleton03.class) {if(INSTANCE == null) {INSTANCE = new Singleton03();}}return INSTANCE;}}}

volatile的作用:
1、保证可见性:修改INSTANCE变量会立即更新到主内存中,其他线程读取INSTANCE变量会从主内存中读取数据,保证实例的唯一性
2、禁止指令重排序:必须先初始化INSTANCE对象然后对象空间赋值给INSTANCE变量,保证实例的完整性
volatile是禁止指令重排序,因为对象创建的顺序是 ① 在堆中创建对象空间 ② 属性默认初始化、构造器初始化 ③ 将堆中的对象空间指向栈中的对象引用,如果没有volatile可能会重排序,先将堆中的对象空间指向栈中的对象引用,此时另一个线程发现对象创建了就直接返回,但此时这个对象还没有属性默认初始化、构造器初始化,是一个不完整的对象
构造器私有化
双端检测double check lock,第一个if判断是否是单例对象是否为空是因为已经创建单例对象了就不会获取锁,第二个if判断是否是单例对象是因为多个线程并发获取锁,获取锁失败的要进来再次初始化,所以用if判断就不用再次初始化了

public class TaskController {@Testpublic void f5() {SingletonFactory.Singleton01 singleton01 = SingletonFactory.Singleton01.getInstance();System.out.println("singleton01 = " + singleton01);singleton01 = SingletonFactory.Singleton01.getInstance();System.out.println("singleton01 = " + singleton01);System.out.println("============================================");SingletonFactory.Singleton02 singleton02 = SingletonFactory.Singleton02.getInstance();System.out.println("singleton02 = " + singleton02);singleton02 = SingletonFactory.Singleton02.getInstance();System.out.println("singleton02 = " + singleton02);System.out.println("============================================");SingletonFactory.Singleton03 singleton03 = SingletonFactory.Singleton03.getInstance();System.out.println("singleton03 = " + singleton03);singleton03 = SingletonFactory.Singleton03.getInstance();System.out.println("singleton03 = " + singleton03);}
}

多次获取同一个类的单例对象

测试结果:
在这里插入图片描述

获取的是同一个对象

2.2 原型模式(prototype)

原型模式是通过拷贝对象来创建新的对象,定义一个抽象类可以定义clone抽象方法,子类重写这个clone方法

程序:

public class JobFactory {abstract public class Job {private String name;public Job(String name) {this.name = name;}public String getName() {return name;}abstract public Job clone();}public class Stu extends Job {public Stu() {super("学生");}@Overridepublic Job clone() {return new Stu();}}public class Teacher extends Job {public Teacher() {super("老师");}@Overridepublic Job clone() {return new Teacher();}}}

定义一个抽象类,定义clone抽象方法,子类重写clone抽象方法

public class TaskController {@Testpublic void f6() {JobFactory jobFactory = new JobFactory();JobFactory.Job job = jobFactory.new Stu();System.out.println("job = " + job);job = job.clone();System.out.println("job = " + job);System.out.println("===========================================");job = jobFactory.new Teacher();System.out.println("job = " + job);job = job.clone();System.out.println("job = " + job);}
}

通过clone方法可以实现原型模式,通过拷贝对象来创建一个新的对象

测试结果:

在这里插入图片描述

原型模式可以创建一个新的对象

2.3 建造者模式(Builder)

建造者模式是将复杂对象的构建过程和表示分离开来,因此同样的构建过程可以创建不同的表示,即不同的属性值

程序:

public class SkillBuilder {private Skill skill = new Skill();public SkillBuilder buildFront(String name) {skill.setFront(name);return this;}public SkillBuilder buildBack(String name) {skill.setBack(name);return this;}public SkillBuilder buildTest(String name) {skill.setTest(name);return this;}public SkillBuilder buildUe(String name) {skill.setUe(name);return this;}public Skill getSkill() {return skill;}public class Skill {private String front;private String back;private String test;private String ue;public void setFront(String front) {this.front = front;}public void setBack(String back) {this.back = back;}public void setTest(String test) {this.test = test;}public void setUe(String ue) {this.ue = ue;}@Overridepublic String toString() {return "Skill{" +"front='" + front + '\'' +", back='" + back + '\'' +", test='" + test + '\'' +", ue='" + ue + '\'' +'}';}}}

创建一个Builder类封装Skill对象属性的构建过程,并可以实现链式调用

public class TaskController {@Testpublic void f7() {SkillBuilder skillBuilder = new SkillBuilder();SkillBuilder.Skill skill = skillBuilder.buildFront("前端").buildBack("后端").buildTest("测试").buildUe("UE").getSkill();System.out.println("skill = " + skill);}
}

通过SkillBuilder可以创建Skill对象,并且是链式调用

测试结果:

在这里插入图片描述

通过建造者模式可以创建一个对象

2.4 工厂方法模式(Factory Method)

工厂方法模式是根据不同的条件创建不同类型的对象,优点是可以通过工厂类创建不同类型的对象,耦合性小

需求:根据不同类型创建不同的形状
分析:不同的类型用if else判断,硬编码

优化思路:设计一个工厂类,根据不同类型创建不同的对象,也可以将对象放到map中然后map.get获取不同类型的对象

程序:

public class ShapeFactory {private Map<String, Shape> map = new HashMap<>();public ShapeFactory() {map.put("rectangle", new Rectangle());map.put("circle", new Circle());}public Shape addShapeByType(String type) {// 根据map查询返回的类型,也可以直接用if else判断类型然后new一个新的对象并返回return map.get(type);}public abstract class Shape {abstract public void draw();}public class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("长方形");}}public class Circle extends Shape {@Overridepublic void draw() {System.out.println("圆形");}}}

工厂类设计一个map,将所有的形状都放到map中,然后map.get就可以获取不同类型的形状

public class TaskController {@Testpublic void f4() {ShapeFactory shapeFactory = new ShapeFactory();ShapeFactory.Shape shape = shapeFactory.addShapeByType("rectangle");System.out.println("shape = " + shape);shape.draw();System.out.println("==========================================");shape = shapeFactory.addShapeByType("circle");System.out.println("shape = " + shape);shape.draw();}
}

入参是形状类型,会返回一个指定类型的形状

测试结果:

在这里插入图片描述

测试结果是可以根据不同的类型创建不同类型的对象

2.5 抽象工厂模式(Abstract Factory)

抽象工厂模式可以创建多组相关的对象,一组中包含多个对象,定义一个抽象类指定创建一组对象,多个实现类创建多组对象,抽象工厂模式可以将使用哪些对象和如何使用这些对象的操作分离开来,优点:用来返回一组对象,耦合性小,增加一组对象会很清晰

需求:根据计算机分辨率大小创建(返回)显示和打印形状的驱动程序
分析:
① 显示形状的驱动程序有LRDD、MRDD、HRDD,打印形状的驱动程序有LRPD、MRPD、HRPD,其中L开头的是低分辨率的,M开头的是中分辨率的,H开头的是高分辨率的,因此将驱动程序分成两类:DisplayDriver、PrintDriver
② 对于不同类型的分辨率可以用if条件判断,但是如果增加一个类型的分辨率会耦合性高

优化思路:设计抽象类是分辨率Resolution,这个抽象类中指定创建DisplayDriver对象和PrintDriver对象等这一组对象,为抽象类的一组对象可以创建多个实现类即计算机分辨率有低、中、高分辨率分别是LowResolution、MiddleResolution、HighResolution,TestController类调用这些类得到一组对象进行操作,因此将使用哪些对象和如何使用这些对象的操作分离开来

程序:

public class ResolutionFactory {// 抽象工厂类Resolution定义一组对象,多个实现类创建多组对象public abstract class Resolution {abstract public DisplayDriver getDisplayDriver();abstract public PrintDriver getPrintDriver();}public class LowResolution extends Resolution {@Overridepublic DisplayDriver getDisplayDriver() {return new DisplayDriver("LRDD");}@Overridepublic PrintDriver getPrintDriver() {return new PrintDriver("LRPD");}}public class MiddleResolution extends Resolution {@Overridepublic DisplayDriver getDisplayDriver() {return new DisplayDriver("MRDD");}@Overridepublic PrintDriver getPrintDriver() {return new PrintDriver("MRPD");}}public class HighResolution extends Resolution {@Overridepublic DisplayDriver getDisplayDriver() {return new DisplayDriver("HRDD");}@Overridepublic PrintDriver getPrintDriver() {return new PrintDriver("HRPD");}}public class DisplayDriver {private String name;public DisplayDriver(String name) {this.name = name;}@Overridepublic String toString() {return "DisplayDriver{" +"name='" + name + '\'' +'}';}}public class PrintDriver {private String name;public PrintDriver(String name) {this.name = name;}@Overridepublic String toString() {return "PrintDriver{" +"name='" + name + '\'' +'}';}}}

抽象工厂类Resolution定义一组对象,多个实现类创建多组对象,一组对象中包含DisplayDriver、PrintDriver

import org.junit.jupiter.api.Test;public class TaskController {@Testpublic void f3() {ResolutionFactory resolutionFactory = new ResolutionFactory();ResolutionFactory.LowResolution lowResolution = resolutionFactory.new LowResolution();ResolutionFactory.DisplayDriver displayDriver = lowResolution.getDisplayDriver();ResolutionFactory.PrintDriver printDriver = lowResolution.getPrintDriver();System.out.println("LowResolution: displayDriver = " + displayDriver + ", printDriver = " + printDriver);ResolutionFactory.MiddleResolution middleResolution = resolutionFactory.new MiddleResolution();displayDriver = middleResolution.getDisplayDriver();printDriver = middleResolution.getPrintDriver();System.out.println("MiddleResolution: displayDriver = " + displayDriver + ", printDriver = " + printDriver);ResolutionFactory.HighResolution highResolution = resolutionFactory.new HighResolution();displayDriver = highResolution.getDisplayDriver();printDriver = highResolution.getPrintDriver();System.out.println("HighResolution: displayDriver = " + displayDriver + ", printDriver = " + printDriver);}
}

TaskController通过getDisplayDriver方法和getPrintDriver方法得到显示驱动程序和打印驱动程序

测试结果是:

在这里插入图片描述

为了得到显示驱动程序和打印驱动程序这一组对象,设置一个抽象类分辨率和多个分辨率实现类,可以创建多组相关的对象

3、结构型模式

3.1 外观模式(Facade)

外观模式是为复杂系统提高一个新的接口,作用是可以简化复杂系统的使用,或者只使用复杂系统的一部分功能,可以封装系统功能,因此用外观模式

程序:

public class ComputerFactory {public class Computer {private Cpu cpu;private Memory memory;private Disk disk;public Computer(Cpu cpu, Memory memory, Disk disk) {this.cpu = cpu;this.memory = memory;this.disk = disk;}public void start() {cpu.start();memory.start();disk.start();System.out.println("Computer started ok");}public void end() {cpu.end();memory.end();disk.end();System.out.println("Computer ended ok");}}public class Cpu {public void start() {System.out.println("Cpu started");}public void end() {System.out.println("Cpu ended");}}public class Memory {public void start() {System.out.println("Memory started");}public void end() {System.out.println("Memory ended");}}public class Disk {public void start() {System.out.println("Disk started");}public void end() {System.out.println("Disk ended");}}}

开启电脑会启动Cpu、Memory、Disk等组件,因此外观模式相当于封装了电脑的各个组件的启动过程

public class TaskController {@Testpublic void f9() {ComputerFactory computerFactory = new ComputerFactory();ComputerFactory.Cpu cpu = computerFactory.new Cpu();ComputerFactory.Memory memory = computerFactory.new Memory();ComputerFactory.Disk disk = computerFactory.new Disk();ComputerFactory.Computer computer = computerFactory.new Computer(cpu, memory, disk);computer.start();System.out.println("================================================");computer.end();}
}

测试结果:

在这里插入图片描述

外观模式可以封装一个复杂对象的启动过程

3.2 适配器模式(Adapter)

适配器模式是将一个接口转换成另一个接口,作用是保证接口是兼容的,保证多态

程序:

public class Shape02Factory {abstract public class Shape {private String name;public Shape(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}abstract public void show();}public class Circle extends Shape {// 适配器模式是组合另一个类private final XXCircle xxCircle;public Circle(String name, XXCircle xxCircle) {super(name);this.xxCircle = xxCircle;}@Overridepublic void show() {xxCircle.display();}}public class XXCircle {public void display() {System.out.println("XXCircle display圆形");}}
}

Circle继承了Shape抽象类,重写了display方法,Circle类组合了XXCircle对象用来调用它的displayIt方法,可以保证多态

public class TaskController {@Testpublic void f8() {Shape02Factory shape02Factory = new Shape02Factory();Shape02Factory.XXCircle xxCircle = shape02Factory.new XXCircle();Shape02Factory.Circle circle = shape02Factory.new Circle("圆形", xxCircle);circle.show();}
}

测试结果:

在这里插入图片描述

适配器模式会调用另一个类的方法

3.3 桥接模式(Bridge)

桥接模式是将抽象和实现解耦,使它们都能独立的变化,寻找可变的参数并封装到一个类中,拆分出抽象和实现,实现作为抽象的一个属性,即用组合代替继承,如果每个抽象都调用具体的实现会出现抽象和实现耦合并且类很多,因此实现也需要抽象出一个抽象类或者接口,每个抽象只需要调用实现的抽象类即可,比如这里的抽象指的是形状,实现指的是画出这个形状需要的画线或者画圆方法,优点:抽象和实现解耦,耦合性小,增加一个抽象或者实现会很清晰

需求:画某个形状的图,比如长方形或者圆形,用多个画图程序(包括画线、画圆)实现画某个形状的图的功能

分析:

在这里插入图片描述

假设现在有多个形状,有多个程序都可以实现每个形状的绘制功能,如果每个形状都创建多个类用来调用多个程序的绘制功能或者每个形状都组合多个程序类来绘制同一个形状,这样会造成类或者组合数量多,并且耦合性高

优化思路:

在这里插入图片描述

寻找可变的参数并封装到一个类中,拆分出抽象和实现,将多个实现封装在一个抽象类中,然后用组合替代继承,将实现的抽象类组合在抽象中
这里抽象出Shape抽象类和Drawing抽象类,Shape抽象类作为抽象,Drawing抽象类作为实现

程序:

public class ShapeFactory {public void process(Shape shape) {shape.draw();}// 形状抽象类是抽象public abstract class Shape {abstract public void draw();}public class Rectangle extends Shape {private int x1, y1, x2, y2;private Drawing drawing;public Rectangle(int x1, int y1, int x2, int y2, Drawing drawing) {this.x1 = x1;this.y1 = y1;this.x2 = x2;this.y2 = y2;this.drawing = drawing;}@Overridepublic void draw() {// Rectangle组合了Drawing实现,避免分别调用V1Drawing和V2Drawing的drawLine方法drawing.drawLine(x1, y1, x1, y2);drawing.drawLine(x1, y2, x2, y2);drawing.drawLine(x2, y2, x2, y1);drawing.drawLine(x2, y1, x1, y1);}}public class Circle extends Shape {private int x, y, r;private Drawing drawing;public Circle(int x, int y, int r, Drawing drawing) {this.x = x;this.y = y;this.r = r;this.drawing = drawing;}@Overridepublic void draw() {// Circle组合了Drawing实现,避免分别调用V1Drawing和V2Drawing的drawCircle方法drawing.drawCircle(x, y, r);}}// 多个绘图程序抽象出绘图抽象类,绘图抽象类就是形状抽象的实现public abstract class Drawing {public abstract void drawLine(int x1, int y1, int x2, int y2);public abstract void drawCircle(int x, int y, int r);}public class V1Drawing extends Drawing {@Overridepublic void drawLine(int x1, int y1, int x2, int y2) {System.out.println("使用绘图程序 1 绘制线条line: x1 " + x1 + " y1 " + y1 + " x2 " + x2 + " y2 " + y2);}@Overridepublic void drawCircle(int x, int y, int r) {System.out.println("使用绘图程序 1 绘制圆circle: x " + x + " y " + y + " r " + r);}}public class V2Drawing extends Drawing {@Overridepublic void drawLine(int x1, int y1, int x2, int y2) {System.out.println("使用绘图程序 2 绘制线条line: x1 " + x1 + " y1 " + y1 + " x2 " + x2 + " y2 " + y2);}@Overridepublic void drawCircle(int x, int y, int r) {System.out.println("使用绘图程序 2 绘制圆circle: x " + x + " y " + y + " r " + r);}}}

ShapeFactory类封装了process方法可以调用形状Shape抽象类的draw方法,实现用Drawing抽象类来表示

其中,画长方形这个形状的draw方法调用了4个drawLine方法

在这里插入图片描述

长方形画图程序有4个点,可以只记录x1,y1,x2,y2这4个参数即可画4条线,4条线的起始点和终止点分别是(x1,y1)和(x1,y2)、(x1,y2)和(x2,y2)、(x2,y2)和(x2,y1)、(x2,y1)和(x1,y1)

优点:增加一个画图方法或者一个形状修改的话会很清晰

import org.junit.jupiter.api.Test;public class TaskController {@Testpublic void f2() {ShapeFactory shapeFactory = new ShapeFactory();ShapeFactory.Drawing drawing = shapeFactory.new V1Drawing();ShapeFactory.Shape shape = shapeFactory.new Rectangle(1,2, 4,6, drawing);shapeFactory.process(shape);System.out.println("=============================================");drawing = shapeFactory.new V2Drawing();shape = shapeFactory.new Rectangle(1, 2, 4, 6, drawing);shapeFactory.process(shape);System.out.println("=============================================");drawing = shapeFactory.new V1Drawing();shape = shapeFactory.new Circle(1,3, 2, drawing);shapeFactory.process(shape);System.out.println("=============================================");drawing = shapeFactory.new V2Drawing();shape = shapeFactory.new Circle(1, 3, 2, drawing);shapeFactory.process(shape);}
}

测试方法会先创建ShapeFactory对象,然后创建Drawing实现,接着创建Shape抽象,因为已经将Drawing实现组合到Shape抽象,因此Shape抽象调用Drawing实现即可,最后调用ShapeFactory对象的process方法执行画图功能

测试结果:

在这里插入图片描述

分别用绘图程序1和2绘制线条line和圆circle

3.4 装饰器模式(Decorator)

装饰器模式可以在对象的之前或者之后动态添加功能,动态调整顺序,不用创建子类就可以动态扩展功能,创建一个对象链实现动态添加功能的效果,如何实现这种链式调用呢?创建一个装饰器抽象类,它包含Component对象,包含的show方法会调用Component对象的show方法,由于java的多态是动态绑定机制,因此会调用运行类型对象的show方法,从而实现Component的多个实现类对象的链式调用,优点:装饰器模式可以在对象的之前或者之后动态添加功能,不用创建子类就可以动态扩展功能,扩展性强,Java的IO流广泛使用了装饰器模式

需求:为销售票据添加表头、页脚等信息
分析:

创建票据对象,创建子类直接在方法开头加上表头,在方法结尾加上页脚,这是硬编码,如果有多个表头和页脚那怎么办,只能创建多个子类即硬编码

优化思路:
采用装饰器模式,创建一个对象链,这个对象链起始于装饰器Decorator对象,终止于原始对象,对象链可以在对象之前或者之后动态添加功能

程序:

public class ComponentFactory {public void show(Component component) {component.show();}abstract public class Component {abstract public void show();}// 原始对象:票据对象SaleTicketpublic class SaleTicket extends Component {@Overridepublic void show() {System.out.println("销售票据SaleTicket");}}// 装饰器抽象类:组合了Component对象,可以调用component的show方法实现链式调用,不用创建子类就可以动态扩展功能abstract public class TicketDecorator extends Component {private Component component;public TicketDecorator(Component component) {this.component = component;}@Overridepublic void show() {// 运行类型(子类)调用show方法component.show();}}public class Header extends TicketDecorator {private String msg;public Header(Component component, String msg) {super(component);this.msg = msg;}@Overridepublic void show() {System.out.println("Header: " + msg);// 调用父类的show方法super.show();}}public class Footer extends TicketDecorator {private String msg;public Footer(Component component, String msg) {super(component);this.msg = msg;}@Overridepublic void show() {// 调用父类的show方法super.show();System.out.println("Footer: " + msg);}}}

创建Component抽象类,创建票据对象SaleTicket,要在这个对象的之前或者之后添加功能,因此SaleTicket对象是原始对象,用装饰器模式,创建装饰器抽象类TicketDecorator,它组合了Component对象,它包含的show方法可以调用Component对象的show方法实现链式调用,因为java的多态是动态绑定机制因此会调用运行类型对象的show方法,装饰器模式的好处是不用创建子类就可以动态扩展功能

public class TaskController {@Testpublic void f1() {ComponentFactory componentFactory = new ComponentFactory();ComponentFactory.SaleTicket saleTicket = componentFactory.new SaleTicket();ComponentFactory.Footer footer = componentFactory.new Footer(saleTicket, "部门yyy");footer = componentFactory.new Footer(footer, "页号1");ComponentFactory.Header header = componentFactory.new Header(footer, "申请人zzz");header = componentFactory.new Header(header, "公司xxx");componentFactory.show(header);}
}

先创建ComponentFactory工厂类对象,然后创建原始对象SaleTicket对象,接着创建Footer对象并将SaleTicket对象组合到其中,然后创建Header对象并将Footer对象组合到其中,最后调用ComponentFactory工厂类对象的show方法会链式调用Component对象的show方法

测试结果:

在这里插入图片描述

测试结果是原始对象销售票据之前添加了两个头部,之后添加了两个页脚,可以动态扩展功能

3.5 享元模式(Flyweight)

享元模式是共享的对象只会创建1次,避免创建大量相似的对象,思路是创建一个享元工厂就是一个map,获取对象的时候会判断如果对象不存在就创建对象并添加到map中,如果对象存在就不用创建对象了直接从map获取

程序:

public class CheseFactory {private Map<String, Chese> map = new HashMap<>();public Chese getChese(String color) {if(map.containsKey(color)) {return map.get(color);}Chese chese = new Chese(color);map.put(color, chese);return chese;}public class Chese {private String color;public Chese(String color) {this.color = color;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}}}

有多个棋子,如果颜色相同不用重复创建,用享元模式,创建一个对象会放到map中

public class TaskController {@Testpublic void f10() {CheseFactory cheseFactory = new CheseFactory();CheseFactory.Chese chese = cheseFactory.getChese("red");System.out.println("chese = " + chese);chese = cheseFactory.getChese("blue");System.out.println("chese = " + chese);chese = cheseFactory.getChese("red");System.out.println("chese = " + chese);chese = cheseFactory.getChese("blue");System.out.println("chese = " + chese);}
}

通过getChese方法传入参数是颜色

测试结果:

在这里插入图片描述

通过getChese方法传入参数如果map中已经存在这个颜色的棋子就会直接返回,不会重复创建,避免创建大量重复对象

3.6 代理模式(Proxy)

代理模式是不改变原始对象的情况下对功能的增强,这样可以保证原始对象的单一职责原则,思路是创建一个代理对象,代理对象中持有了原始对象的引用

程序:

public class SportsFactory {public interface Sports {public void doSports();}public class SportsImpl implements Sports {@Overridepublic void doSports() {System.out.println("doSports ing~");}}public class JDKInvocationHandler implements InvocationHandler {private Object target;public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before doSports: buy water");Object res = method.invoke(target, args);System.out.println("after doSports: take a shower");return res;}public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}}}

创建一个接口,创建一个实现类,创建一个JDK动态代理对象,实现InvocationHandler接口,重写invoke方法,用Proxy.newProxyInstance方法创建基于接口的JDK动态代理对象

@Test
public void f12() {SportsFactory sportsFactory = new SportsFactory();SportsFactory.Sports sports = sportsFactory.new SportsImpl();SportsFactory.JDKInvocationHandler jdkInvocationHandler = sportsFactory.new JDKInvocationHandler(sports);// 创建JDK动态代理对象sports = (SportsFactory.Sports) jdkInvocationHandler.getProxy();sports.doSports();
}

创建基于接口的JDK动态代理对象,JDK代理对象和实现类对象是兄弟的关系,因为它们都实现了同一个接口

测试结果:

在这里插入图片描述

JDK动态代理可以在实现类的方法之前和之后分别输出,这是为了保证设计模式中一个类的单一职责原则

3.7 组合模式(Composite)

组合模式是将对象组合成树状结构表示树状的层次关系,思路是创建一个抽象类,创建目录和文件,目录中可以包含文件或者子目录,因此目录这个类中包含一个List集合

程序:

public class CompositeFactory {abstract public class Component {private String name;public Component(String name) {this.name = name;}abstract public void addComponent(Component component);abstract public void show();}public class File extends Component {public File(String name) {super(name);}@Overridepublic void addComponent(Component component) {throw new RuntimeException("文件不能添加子元素");}@Overridepublic void show() {System.out.println("File name = " + super.name);}@Overridepublic String toString() {return " { File name = " + super.name + " } ";}}public class Dict extends Component {private List<Component> list = new ArrayList<>();public Dict(String name) {super(name);}@Overridepublic void addComponent(Component component) {list.add(component);}@Overridepublic void show() {// 将list集合中的Component对象转成String字符串并按照逗号分隔String s = list.stream().map(component -> component.toString()).collect(Collectors.joining(","));System.out.println("Dict name = " + super.name + ",它包含了list = " + s);}@Overridepublic String toString() {return " { Dict name = " + super.name + ", list = " + list.toString() + " } ";}}}

创建一个抽象类,创建文件和目录,目录中可以存放子目录和文件,因此用一个List集合存放它们

@Test
public void f11() {CompositeFactory compositeFactory = new CompositeFactory();CompositeFactory.File file01 = compositeFactory.new File("file01");CompositeFactory.File file02 = compositeFactory.new File("file02");CompositeFactory.File file03 = compositeFactory.new File("file03");CompositeFactory.File file04 = compositeFactory.new File("file04");CompositeFactory.Dict dict01 = compositeFactory.new Dict("dict01");CompositeFactory.Dict dict02 = compositeFactory.new Dict("dict02");CompositeFactory.Dict dict03 = compositeFactory.new Dict("dict03");dict01.addComponent(dict02);dict01.addComponent(dict03);dict02.addComponent(file01);dict02.addComponent(file02);dict03.addComponent(file03);dict03.addComponent(file04);dict01.show();
}

创建树状结构

测试结果:

在这里插入图片描述

组合模式可以展示树状结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397988.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sliver源码分析-初始化以及脚手架

引言 项目概述&#xff1a;对开源的C2框架sliver进行源码分析&#xff0c;意图学习其原理。本篇分析sliver的入口以及脚手架&#xff0c;和基本的配置文件目标与读者&#xff1a;网络安全兴趣爱好者 准备工作 源码路径BishopFox/sliver: Adversary Emulation Framework (git…

[C++][opencv]基于opencv实现photoshop算法图像旋转

【测试环境】 vs2019 opencv4.8.0 【效果演示】 【核心实现代码】 //图像旋转: src为原图像&#xff0c; dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式 int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip) {Mat input src.…

【c++】类和对象 (中) (类的默认成员函数)

类的默认成员函数 在C中&#xff0c;如果你定义了一个类但没有显式地提供特定的成员函数&#xff08;比如构造函数、析构函数、拷贝构造函数、拷贝赋值运算符等&#xff09;&#xff0c;编译器会为这些函数生成默认的实现。这些默认生成的成员函数称为类的默认成员函数。那么既…

C#学习笔记15:上位机助手_usercontrol窗体内嵌的应用

今日完善一下之前的上位机助手&#xff0c;做一个组合窗体内嵌的多功能助手软件应用, 与之前的上位机软件相比: 更注重控件能够随着窗体缩放而缩放变换&#xff0c;串口助手部分能自动后台检测串口设备&#xff0c;解决市面上大部分串口助手的打开初始化会卡顿的问题 ( 多线程后…

Linux服务管理-Nginx配置

静态解析主要解析html、css动态解析需要解析php 动态资源通过轮询分配到后端的Apache服务器处理 apache是同步阻塞&#xff0c;nginx是异步非阻塞

论文阅读笔记:Efficient Teacher: Semi-Supervised Object Detection for YOLOv5

Efficient Teacher: Semi-Supervised Object Detection for YOLOv5 1 背景1.1 动机1.2 问题 2 创新点3 方法4 模块4.1 伪标签分配4.2 Epoch Adapter 5 效果5.1 与SOTA方法对比5.2 消融实验 论文&#xff1a;https://arxiv.org/pdf/2302.07577v3.pdf 代码&#xff1a;https://g…

Python 常用内置函数

目录 1、enumerate函数 1.1、for循环中使用 1.2、enumerate指定索引的起始值 1.3、enumerate在线程中的作用 2、Map 函数 2.1、map()函数可以传多个迭代器对象 3、lambda表达式&#xff08;匿名函数&#xff09; 示例 4、sort函数和sorted函数 4.1、sort()函数 4.2、…

map和set的使用

关联式容器 在学习关联式容器之前&#xff0c;我们学习过的容器有vector、list、deque…这些容器称为序列式容器&#xff0c;单纯的存储数据存储的数据没有关联性。 即将学习的map 和set属于关联式容器&#xff0c;其里面存储的是<key, value>结构的键值对&#xff0c;…

制造知识普及(九)--企业内部物料编码(IPN)与制造商物料编码(MPN)

在日常的物料管理业务逻辑中&#xff0c;一物一码是物料管理的基本的业务规则&#xff0c;不管物料从产品开发还是仓库管理&#xff0c;甚至成本核算&#xff0c;都要遵循这个原则&#xff0c;才能保证产品数据的准确性&#xff0c;才具备唯一追溯的可行性。大部分企业都是这种…

某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏洞复现 [附POC]

文章目录 某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现0x06 修复建议某通电子文档安全管理系统 CDGAuthoriseTempletService1接口SQL注入漏…

C#数据类型转换

代码&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace Test05 {class Program{static void Main(string[] args){double db 2008;//声明一个double类型变量db&#xff0c;并初始化为2008object obj db;//对db…

JAVA实现判断小程序用户是否关注公众号

本文主要描述了判断小程序用户是否关注公众号的逻辑实现及部分代码 首先阐述一下大致流程&#xff1a; 1、在将小程序和公众号绑定至同一个微信开发平台下&#xff1b; 2、后端拉取公众号已关注用户列表&#xff0c;并获取其中每一个用户的unionID&#xff0c; 建立已关注用户…

OCR调研

OCR调研 一、介绍 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是一种将图像中的文字转换为计算机可处理格式的技术。OCR技术经历了从传统OCR到基于深度学习的OCR的转变。深度学习OCR技术通过模拟人脑神经元结构处理文本和图像数据&am…

MATLAB - 强化学习(Reinforcement Learning)

系列文章目录 前言 一、什么是强化学习&#xff1f; 强化学习是一种以目标为导向的计算方法&#xff0c;计算机通过与未知的动态环境交互来学习执行任务。这种学习方法能让计算机在没有人工干预和明确编程的情况下&#xff0c;做出一系列决策&#xff0c;使任务的累积奖励最大化…

cmake 编译教程

参考链接&#xff1a;cmake使用详细教程&#xff08;日常使用这一篇就足够了&#xff09;_cmake教程-CSDN博客 一、只有一个源文件的程序编译 首先在当前目录下创建两个文件 hello.cpp CMakeLists.txt &#xff08;注意CMakeLists大小写&#xff0c;不要写错了&#xff09; …

推荐一个优秀的 .NET MAUI 组件库

目录 前言 组件介绍 组件展示 布局 按钮 复选框 进度条 导航栏 组件地址 最后 前言 .NET MAUI 的发布&#xff0c;项目中可以使用这个新的跨平台 UI 框架来轻松搭建的移动和桌面应用。 为了帮助大家更快地构建美观且功能丰富的应用&#xff0c;本文将推荐一款优秀…

AcCode核心思路

文章目录 在线OJ项目核心思路1. 项目介绍2.预备知识理解多进程编程为啥采用多进程而不使用多线程?标准输入&标准输出&标准错误 3.项目实现题目API实现相关实体类定义新增/修改题目获取题目列表 编译运行编译运行流程 4.统一功能处理 在线OJ项目核心思路 1. 项目介绍 …

有序转化数组(LeetCode)

题目 给你一个已经 排好序 的整数数组 和整数 、 、 。对于数组中的每一个元素 &#xff0c;计算函数值 &#xff0c;请 按升序返回数组 。 解题 在时间复杂度为解决问题 def sortTransformedArray(nums, a, b, c):def f(x):return a * x * x b * x cn len(nums)result…

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…

学生公寓电费信息管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公寓管理员管理&#xff0c;学生管理&#xff0c;楼层信息管理&#xff0c;用电情况管理&#xff0c;缴费清单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;用电情况…