当前位置: 首页 > news >正文

设计模式(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();
}

创建树状结构

测试结果:

在这里插入图片描述

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

相关文章:

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

1、目标 本文的主要目标是学习创建型模式和结构型模式&#xff0c;并分别代码实现每种设计模式 2、创建型模式 2.1 单例模式&#xff08;singleton&#xff09; 单例模式是创建一个对象保证只有这个类的唯一实例&#xff0c;单例模式分为饿汉式和懒汉式&#xff0c;饿汉式是…...

RuoYi-Vue新建模块

一、环境准备 附:RuoYi-Vue下载与运行 二、新建模块 在RuoYi-Vue下新建模块ruoyi-test。 三、父pom文件添加子模块 在RuoYi-Vue的pom.xml中,引入子模块。 <dependency><groupId>com.ruoyi</groupId><artifactId>ruoyi-test</artifactId>&…...

Element-UI自学实践

概述 Element-UI 是由饿了么前端团队推出的一款基于 Vue.js 2.0 的桌面端 UI 组件库。它为开发者提供了一套完整、易用、美观的组件解决方案&#xff0c;极大地提升了前端开发的效率和质量。本文为自学实践记录&#xff0c;详细内容见 &#x1f4da; ElementUI官网 1. 基础组…...

ChatGPT如何工作:创作一首诗的过程

疑问 怎样理解 Chat GPT 的工作原理&#xff1f;比如我让他作一首诗&#xff0c;他是如何创作的呢&#xff1f;每一行诗&#xff0c;每一个字都是怎么来的&#xff1f;随机拼凑的还是从哪里借鉴的&#xff1f; 回答 当你让 ChatGPT 创作一首诗时&#xff0c;它并不是简单地随…...

Linux_Shell变量及运算符-05

一、Shell基础 1.1 什么是shell Shell脚本语言是实现Linux/UNIX系统管理及自W动化运维所必备的重要工具&#xff0c; Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算…...

OpenCV图像滤波(13)均值迁移滤波函数pyrMeanShiftFiltering()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 函数执行均值迁移图像分割的初始步骤。 该函数实现了均值迁移分割的过滤阶段&#xff0c;即输出是经过滤波的“海报化”图像&#xff0c;其中颜色…...

用爬虫技术探索石墨文档:数据自动化处理与个性化应用的创新实践

用爬虫技术探索石墨文档&#xff1a;数据自动化处理与个性化应用的创新实践 在当今这个信息爆炸的时代&#xff0c;文档管理与协作成为了企业运营和个人工作中不可或缺的一部分。石墨文档&#xff0c;作为一款轻量级的云端Office套件&#xff0c;凭借其强大的在线协作、实时同…...

【JavaEE初阶】线程池

目录 &#x1f4d5; 引言 &#x1f333; 概念 &#x1f340;ThreadPoolExecutor 类 &#x1f6a9; int corePoolSize与int maximumPoolSize&#xff1a; &#x1f6a9; long keepAliveTime与TimeUnit nuit&#xff1a; &#x1f6a9; BlockingQueue workQueue&#xff1a…...

zdpgo_cobra_req 新增解析请求体内容

zdpgo_cobra_req 使用Go语言开发的&#xff0c;类似于curl的HTTP客户端请求工具&#xff0c;用于便捷的测试各种HTTP地址 特性 1、帮助文档都是中文的2、支持常见的HTTP请求&#xff0c;比如GET、POST、PUT、DELETE等 下载 git clone https://github.com/zhangdapeng520/z…...

Java聚合快递对接云洋系统快递小程序源码

&#x1f31f;【一键聚合&#xff0c;高效便捷】快递对接云洋系统小程序全攻略&#x1f680; 引言&#xff1a;告别繁琐&#xff0c;拥抱智能快递新时代&#x1f50d; 在这个快节奏的时代&#xff0c;每一分每一秒都弥足珍贵。你是否还在为手动输入多个快递单号、频繁切换不同…...

陕西西安培华学院计算机软件工程毕业设计课题选题参考目录​

陕西西安培华学院计算机软件工程毕业设计课题选题 博主介绍&#xff1a;✌️大厂码农|毕设布道师&#xff0c;阿里云开发社区乘风者计划专家博主&#xff0c;CSDN平台&#xff0c;✌️Java领域优质创作者,博客之星、掘金/华为云/阿里云等平台优质作者、专注于大学生项目实战开发…...

如何用sql在1分钟从1T数据中精准定位查询?Hive离线数仓 Spark分析

最近在一个群里&#xff0c;从群友哪里了解到这样一个业务需求&#xff1a;如何在 hdfs 1 T源数据库中&#xff0c;1分钟内从其中抓取自己想要的数据&#xff1f; 我的理解是 &#xff1a; 在hdfs数据库中为拥有 尽1T数据的表创建索引,并对其进行性能优化&#xff0c;以实现…...

acpi 主板布局需要 efi

今天在折腾 ESXI 的时候&#xff0c;启动虚拟机跳出了 acpi 主板布局需要 efi 然后我就将 ESXI 的启动方式改为了 EFI 但是虚拟机有莫名的启动不了&#xff0c;网上也没有找到办法&#xff0c;最后&#xff0c;我将虚拟机类型有原本的 ubuntu 换成了 debian 最后启动成功&…...

月之暗面对谈 Zilliz:长文本和 RAG 如何选择?

01 长文本与RAG通用对比 准确率&#xff1a;通常情况下长文本优于RAG 长文本&#xff1a;可更加综合的去分析所有相关的内容&#xff0c;提取相关数字&#xff0c;生成图表&#xff0c;效果尚可。RAG&#xff1a;更适合找到一段或者是几段可能相关的段落。如果希望大模型能够…...

高级java每日一道面试题-2024年8月12日-设计模式篇-请列举出在JDK中几个常用的设计模式?

如果有遗漏,评论区告诉我进行补充 面试官: 请列举出在JDK中几个常用的设计模式? 我回答: 在Java Development Kit (JDK) 中&#xff0c;许多设计模式被广泛使用&#xff0c;以帮助实现软件的结构、行为和复用。下面是一些在JDK中常见的设计模式及其简要说明&#xff1a; 工…...

mysql workbench8.0如何导出mysql5.7格式的sql定义

碰到的问题 mac上安装mysql workbech6.0后不能运行&#xff0c;但安装workbench8.0后&#xff0c;导出的数据库sql文件默认是msyql 8.0的语法和格式。比如生成索引的语句后面会有visible关键字&#xff0c;当把mysql8.0的sql文件导入到mysql5.7时就会报错。 如何解决 点击my…...

数据结构(学习)2024.8.6(顺序表)

今天开始学习数据结构的相关知识&#xff0c;大概分为了解数据结构、算法&#xff1b;学习线性表&#xff1a;顺序表、链表、栈、队列的相关知识和树&#xff1a;二叉树、遍历、创建&#xff0c;查询方法、排序方式等。 目录 一、数据结构 数据 逻辑结构 1.线性结构 2.树…...

MyBatis全解

目录 一&#xff0c; MyBatis 概述 1.1-介绍 MyBatis 的历史和发展 1.2-MyBatis 的特点和优势 1.3-MyBatis 与 JDBC 的对比 1.4-MyBatis 与其他 ORM 框架的对比 二&#xff0c; 快速入门 2.1-环境搭建 2.2-第一个 MyBatis 应用程序 2.3-配置文件详解 (mybatis-config.…...

【Redis进阶】Redis集群

目录 Redis集群的诞生 单节点Redis的局限性 1.存储容量限制 2.性能瓶颈 3.单点故障 4.扩展性能差 分布式系统发展的需要 1.海量数据处理 2.高性能要求 3.弹性扩展能力 Redis集群&#xff08;cluster&#xff09; 如图所示案例 Redis集群设计 什么是数据分片&…...

JVM运行时数据区之虚拟机栈

【1】概述 Java虚拟机栈&#xff08;Java Virtual Machine Stack&#xff09;&#xff0c;早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈&#xff0c;其内部保存一个个的栈帧&#xff08;Stack Frame&#xff09;&#xff0c;对应着一次次的Java方法调用。 栈是运行…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...