设计模式-创建型模式
1.单例模式
懒汉式
-
类加载顺序
-
开辟空间
-
初始化空间
-
赋值对象
-
Demo demo = new Demo();
-
-
可能因为
编译器,JIT,CPU
的原因,上面的顺序坑能会被打乱,导致指令重排(执行结果不会被改变),那要怎么解决呢? -
在变量上添加关键字
volatile
-
private volatile static LazyDemo lazyDemo;
-
volatile
修饰的变量不会被指令重排
-
饿汉式
-
直接上代码
-
public class HungryType { private static final HungryType MY_UTIL = new HungryType(); private HungryType(){} public static HungryType getInstance(){ return MY_UTIL; } }
工厂模式
工厂方法
创建型方式, 省去了new 的过程
-
定义抽象产品
-
抽象产品方法
-
abstract class Car{ abstract void run (); }
-
-
充当具体的产品角色
-
/** * 角色1 */ class Bmw extends Car { @Override void run() { System.out.println("我是BMW"); } } /** * 角色2 */ class BenChi extends Car{ @Override void run(){ System.out.println("我是奔驰"); } }
-
定义抽象工厂
-
/** * 抽象工厂 */ abstract class CarFactory{ abstract Car productCar(); public void run(){ productCar().run(); } }
-
-
定义工厂角色
-
/** * BenChi工厂角色 */ class BenChiFactoryItem extends CarFactory{ @Override Car productCar() { return new BenChi(); } } /** * Bmw工厂角色 */ class BmwFactoryItem extends CarFactory{ @Override Car productCar() { return new Bmw(); } }
-
-
运行测试
-
public static void main(String[] args) { //运行奔驰 CarFactory benChiFactoryItem = new BenChiFactoryItem(); benChiFactoryItem.run(); //运行宝马 CarFactory bmwFactoryItem = new BmwFactoryItem(); bmwFactoryItem.run(); }
-
-
由此得出工厂方法所需的几个角色
- 抽象产品角色
- 具体的产品角色
- 抽象工厂角色
- 具体的抽象工厂角色
-
缺点
- 添加新产品会增加具体工厂类和具体产品类,这无疑会增加系统的复杂度,带来更多的开销
-
优点
- 所有的具体工厂类都具有同一抽象父类
- 所有的具体抽象角色都有共同的抽象父类,比如这里的
CarFactory
就是BmwFactoryItem
和BenChiFactoryItem
的父类,具体细节完全分装在具体抽象角色中. - 如果加入新的产品角色,只需要添加一个具体工厂和一个具体产品,无需修改之前代码,符合
开闭原则
建造者模式
-
创建抽象得到建造者
-
abstract class Build { public abstract Build buildA(String msg); public abstract Build buildB(String msg); public abstract Build buildC(String msg); public abstract Build buildD(String msg); public abstract Product getProduct(); }
-
抽象出要建造什么产品
-
由工人来实现这些产品的创建顺序以及内容
-
-
创建产品
-
class Product{ private String buildA = "汉堡"; private String buildB = "可乐"; private String buildC = "竖条"; @Override public String toString() { return "Product{" + "buildA='" + buildA + '\'' + ", buildB='" + buildB + '\'' + ", buildC='" + buildC + '\'' + ", buildD='" + buildD + '\'' + '}'; } public String getBuildA() { return buildA; } public void setBuildA(String buildA) { this.buildA = buildA; } public String getBuildB() { return buildB; } public void setBuildB(String buildB) { this.buildB = buildB; } public String getBuildC() { return buildC; } public void setBuildC(String buildC) { this.buildC = buildC; } public String getBuildD() { return buildD; } public void setBuildD(String buildD) { this.buildD = buildD; } private String buildD = "甜点"; }
-
这里的产品可以有默认值, 当调用是传入值的时候就会使用传入的内容,调用时没有传值就会使用默认值
-
-
创建工人 worker
-
class Worker extends Build{ private final Product product; public Worker(){ product = new Product(); } @Override public Build buildA(String msg) { product.setBuildA(msg); return this; } @Override public Build buildB(String msg) { product.setBuildA(msg); return this; } @Override public Build buildC(String msg) { product.setBuildA(msg); return this; } @Override public Build buildD(String msg) { product.setBuildA(msg); return this; } @Override public Product getProduct() { return product; } }
-
工人来决定创建顺序以及内容
-
因为每个
build方法
返回的都是this
,所以当调用的时候可以实现链式调用
-
-
测试
-
public static void main(String[] args) { Worker woker = new Worker(); //直接调用有默认值 //Product product = woker.getProduct(); Product product = woker.buildA("a").buildB("b").buildC("C").buildD("D").getProduct(); System.out.println("product = " + product); }
-
由于工人(
Worker
)里面的实现方法返回的this
,所以这里可以实现链式调用
-
-
角色
- 建造者 Build
- 产品 Product
- 工人 Worker
-
缺点
- 如果产品之间差异性过大,不适用建造者模式,因此使用范围受到限制。
- 如果内部变化复杂,则需要很多建造者实现这种变化,导致系统变得很庞大。
-
优点
- 产品的建造和表示分离实现了解耦。
- 建造者模式可以使客户端不必知道内部组成的细节。
- 将复杂的产品的创建步骤分解在不同的方法中,是创建过程更清晰。
- 具体的建造者之间相互独立,有利于扩展,增加新的建造者无需修改原有的类库,符合
开闭原则
-
使用场景
- 产品对象有
共同
的特性 - 隔离对象创建和使用,并使得相同的创建过程创建不同的场景
- 产品对象有
原型模式
-
原型模式本质上就是对象克隆
-
基本使用
-
public class Dept implements Cloneable{ private String name; private Date date; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } @Override public String toString() { return "Dept{" + "name='" + name + '\'' + ", date=" + date + '}'; } public void setDate(Date date) { this.date = date; } }
-
Date date = new Date(); Dept dept = new Dept(); dept.setDate(date); dept.setName("zhangsan"); System.out.println("dept = " + dept); System.out.println("dept = " + dept.hashCode()); Dept clone = (Dept) dept.clone(); System.out.println("clone = " + clone); System.out.println("clone = " + clone.hashCode());
-
这样可以实现克隆,但是两个
Dept
应用的是同一个Date
,如果Date
被改变,那么原有的Dept
和克隆的Dept
都会被改变. 只是实现了浅克隆 -
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l6dU1ZZz-1657594095892)(C:\Users\mucd\AppData\Roaming\Typora\typora-user-images\1657442494931.png)]
-
-
修改
Dept
的clone
方法,实现对象深克隆-
public class Dept implements Cloneable { private String name; private Date date; @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); Dept dept = (Dept) obj; date = (Date) date.clone(); return dept; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getDate() { return date; } @Override public String toString() { return "Dept{" + "name='" + name + '\'' + ", date=" + date + '}'; } public void setDate(Date date) { this.date = date; } }
-
public static void main(String[] args) throws CloneNotSupportedException { Date date = new Date(); Dept dept = new Dept(); dept.setDate(date); dept.setName("zhangsan"); System.out.println("dept = " + dept); System.out.println("dept = " + dept.hashCode()); //修改时间 date.setTime(220020); //克隆出来的对象不会被改变 Dept clone = (Dept) dept.clone(); System.out.println("clone = " + clone); System.out.println("clone = " + clone.hashCode()); }
-
Dept的clone方法是重点
-
代理模式
-
角色
- 抽象角色
- 真实角色,被代理的
- 代理角色,代理真实角色,有一些附属操作
- 客户,访问代理角色
-
实现
-
抽象角色
-
pubic interface Animal{ void eat(); void call(); }
-
-
真实角色,被代理的角色
-
public class Cat implements Animal{ @Override public void eat() { System.out.println("cat eat ..."); } @Override public void call() { System.out.println("cat call ..."); } }
-
被代理的都是原有的功能
-
-
代理角色,代理类,代理真实角色,通常具有附属操作
-
public class AnimalProxy implements Animal{ private Animal animal; public void setAnimal(Animal animal) { this.animal = animal; } @Override public void eat() { newMethod("大吃"); animal.eat(); } @Override public void call() { newMethod("大叫"); animl.call(); } // 附属操作 public void newMethod(String msg){ System.out.println("Debug:"+new Date()+" "+msg); } }
-
-
测试
-
public class Main02 { public static void main(String[] args) { Cat cat = new Cat(); AnimalProxy animalProxy = new AnimalProxy(); animalProxy.setAnimal(cat); animalProxy.call(); animalProxy.eat(); } }
-
运行的是代理类, 把被代理的
Cat
给set
进了CatProxy
-
-
扩展
-
如果此时我想添加一个
Dog
类,那么实现方式如下 -
首先创建一个被代理的
Dog
类,同样实现Animal
-
public class Dog implements Animal{ @Override public void eat() { System.out.println("dog eat ..."); } @Override public void call() { System.out.println("dog call ..."); } }
-
AnimalProxy
代理类不用改动 -
运行测试
-
public static void main(String[] args) { Cat cat = new Cat(); AnimalProxy animalProxy = new AnimalProxy(); animalProxy.setAnimal(cat); animalProxy.call(); animalProxy.eat(); Dog dog = new Dog();; animalProxy.setAnimal(dog); animalProxy.call(); animalProxy.eat(); }
-
-
动态代理
-
动态代理角色和静态代理相同
-
党台代理雷士动态生成的
-
实现动态代理的几种分类
- 基于接口
- JDK
- 基于类
- cglib
- java字节码
- java ssist
- 基于接口
-
实现
-
抽象角色
-
public interface UserService { void add(); void delete(); void update(); }
-
-
真实角色
-
public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("add ...."); } @Override public void delete() { System.out.println("delete ...."); } @Override public void update() { System.out.println("update ...."); } }
-
-
代理角色,代理工具类
-
public class UserProxyHandler implements InvocationHandler { /** * 传入谁就代理谁 */ private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代理方法执行之前要执行内容 log(method.getName()); //使用反射执行target中的方法,args是方法的参数 Object invoke = method.invoke(target, args); return invoke; } /** * 获取到被代理类 * @return 被代理对象 */ public Object getProxyHandler(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /** * 横切方法 * @param msg */ public void log(String msg) { System.out.println(new Date()+","+msg); } }
-
-
运行测试
-
public static void main(String[] args) { //被代理角色 UserServiceImpl userService = new UserServiceImpl(); UserProxyHandler userProxyHandler = new UserProxyHandler(); userProxyHandler.setTarget(userService); UserService proxyHandler1 = (UserService) userProxyHandler.getProxyHandler(); proxyHandler1.delete(); }
-
-
结论
-
在第4步的代理工具类中可以看到,
setTarget
方法传入什么对象,那么就会自动的代理什么对象,那么就不需要一个角色一个代理,大大减少代码量
-
-