设计模式
文章目录
- 设计模式
- 创建型设计模式
- 1单例模式(重点)
- static
- 1.1饿汉模式
- 懒加载
- 1.2懒汉模式
- 解决懒汉模式在多线程中的错误
- 1.3静态内部类单例模式
- 1.4枚举单例
- 2简单工厂模式
- **抽象产品类**:
- **具体产品类**:
- **工厂类**:
- **客户端调用工厂类:**
- 3工厂方法模式(重点)
- **抽象产品类**
- 具体产品类
- 抽象工厂
- 具体工厂
- 客户端调用工厂类
- 4建造者模式(重点)
- 结构型设计
- 1代理模式
- 2动态代理模式(重点)
- 抽象主题类
- 真实主题类
- 客户端调用
- 3装饰模式
- 抽象组件
- 组件实现具体类
- 抽象装饰者
- 抽象装饰者实现具体类
- 客户端调用
- 4外观模式
- 子系统类
- 外观类
- 客户端调用
- 5享元模式(重点)
- 抽象享元角色
- 具体享元角色
- 享元工厂
- 客户端调用
- 行为型设计模式
- 1.观察者模式(重点)
- 抽象观察者
- 具体观察者(是谁)
- 抽象被观察者
- 具体被观察者(要做的事情)
- 客户端
- 简单总结一下
- 工厂方法模式使用场景
- 建造者模式的使用场景
- 动态代理模式的使用场景
- 享元模式的使用场景
- 观察者模式的使用场景
创建型设计模式
与对象创建有关
(图片来源网络,侵删)1单例模式(重点)
单例模式的意思就是一个类只有一个实例,并提供一个访问它的全局访问点
大的来说,可以分为4种(因为我感觉双重DCL也算是懒汉模式)
(图片来源网络,侵删)- 饿汉模式
- 懒汉模式
- 静态内部类单例模式
- 枚举单例
先说一下static这个修饰符
static
如果一个变量被static修饰,那么它就属于类的静态变量,也称为类变量。类变量在类加载时被初始化,它存储在方法区中,是所有实例共享的,因此多个线程调用这个变量时,调用的都是同一个变量。
1.1饿汉模式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }
首先一个重点就是类的初始化得在一开始就弄,并且是由static修饰
使用static关键字创建类的静态变量时,这个变量会在类加载时被初始化。在java中,类加载只会发生1次。因此,如果使用static关键字创建单例对象,那么在类加载时就会创建单例对象。
但是饿汉模式是有一个缺点,就是这个变量会在类加载时被初始化,如果没用过这个实例的话,就容易造成内存的浪费。
这就不符合懒加载的效果
懒加载
懒加载(Lazy Loading)是一种延迟加载的策略,它指的是在需要使用某个对象时才进行加载和初始化,而不是在程序启动时就进行加载和初始化。
懒加载的主要目的是为了提高程序的性能和资源利用率。如果一个对象在程序启动时就被创建和初始化,但在程序运行过程中又没有被使用,那么就会造成资源浪费。而如果使用懒加载的策略,只有在需要使用该对象时才进行加载和初始化,就可以避免这种资源浪费。
而懒汉模式就刚好符合了这个原理
1.2懒汉模式
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if(instance==null) { instance = new Singleton(); } return instance; } }
当调用**getInstance()**这个方法的时候,instance才会开始初始化,避免了内存的浪费。
但是出现了一个问题,如果在单线程中,调用的是唯一的instance,但是在多线程中不一定了,具体来说,当多个线程同时访问懒汉模式的getInstance()方法时,如果此时还没有创建单例对象,那么就会同时进入到if语句块中,从而导致多个线程创建多个实例的问题。这种情况下,就会导致单例对象的失效,从而引发一系列的问题。
解决懒汉模式在多线程中的错误
一种情况就是用synchronized将**getInstance()**这个方法锁住,
如下:
public class Singleton { private static Singleton instance; private Singleton() {} public static sychronized Singleton getInstance() { if(instance==null) { instance = new Singleton(); } return instance; } }
但是用多线程的线程同步有一个可见性, 用sychronized保证了线程同步,也就保证了可见性,也就造成了数据的同步,但有时我们并不需要数据同步,遇到不需要数据同步的时候就不要考虑懒汉模式了。
另一种解决懒汉模式在多线程中的错误是DCL双重检查模式
这段代码在线程同步那块写过
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
chatgpt对两个if作用的解释:
第一个if用于检查实例是否已经被创建,如果实例已经被创建,则直接返回已经创建的实例,避免重复创建实例。如果实例尚未创建,则进入同步代码块进行实例的创建,这里需要再次判断实例是否已经被创建,以防止其他线程在等待锁的过程中又创建了实例。
第二个if判断的作用是为了确保在同步代码块中只有一个线程能够创建实例。在多线程环境下,如果没有第二个if判断,可能会出现多个线程同时创建实例的情况,从而导致程序的错误。
我自己的理解
假如有a,b两个线程,a线程instancenull,b的instancenull,c的instance!=null
那么只有a,b进入第二个if判断,c就不用了(这就是第一个判断的作用,如果不使用第一个if判断,每次调用getInstance方法都会进入同步代码块,会增加同步开销,降低程序的执行效率)
第二个if判断,因为有了sychronized将方法锁住,所以a,b不是同时进入,而是一个一个按顺序进入,只要有一个线程创建了,其他线程也就不会创建了。
然后就是那个volatile关键字,之前说过它只能保证有序性和可见性,没办法保证原子性。
这里面是因为sychronized是锁在getInstance() 方法里面的,而不是外面。外面的话就不用volatile修饰了,但是这个是锁在里面的,如果没有volatile就没办法保证第一个if()判断中instance满足有序性和可见性。
讲一个准确率最高的方法实现单例模式
1.3静态内部类单例模式
public class Singleton{ private Singlenton(){ } public static Singleton getInstance(){ return SingletonHolder.sInstance; } private static class SingletonHolder{ private static final Singleton sInstance = new Singleton(); } }
第一次加载Singleton类的时候并不会初始化sInstance,只有在调用**getInstance()**才会初始化。
1.4枚举单例
public enum Singleton { INSTANCE; // 添加自己需要的属性和方法 }
在Java中,枚举类型是一种特殊的类,它可以定义若干个枚举常量,并且枚举常量的数量是固定的。使用枚举单例时,只需要定义一个枚举类型,其中只包含一个枚举常量,该枚举常量就是单例实例。由于枚举类型的特殊性,它的构造方法默认是私有的,因此无法从外部创建新的枚举常量,保证了单例的唯一性。
接下来说另2种创建型设计模式
但是这2种创建型设计模式我感觉是一种,第二种是第一种的优化
2简单工厂模式
首先是
抽象产品类:
public abstract class computer { public abstract void start(); }
其次是
具体产品类:
public class Lenovo extends computer{ @Override public void start() { System.out.println("联想电脑启动"); } }
public class ROG extends computer{ @Override public void start() { System.out.println("时代的王,世界的狂"); } }
public class HP extends computer{ @Override public void start() { System.out.println("夏普电脑启动"); } }
然后是
工厂类:
public class ComputerFactory { public static void createComputer(String type){ computer computer = null; switch (type){ case "ROG": computer = new ROG(); computer.start(); break; case "HP": computer = new HP(); computer.start(); break; case "Lenovo": computer = new Lenovo(); computer.start(); break; } } }
最后是:
客户端调用工厂类:
public class User { public static void main(String[] args) { ComputerFactory.createComputer("ROG"); ComputerFactory.createComputer("Lenovo"); ComputerFactory.createComputer("HP"); } }
最后输出:
时代的王,世界的狂
联想电脑启动
惠普电脑启动
3工厂方法模式(重点)
抽象产品类
public abstract class Computer { public abstract void start(); }
具体产品类
public class HP extends Computer{ @Override public void start() { System.out.println("惠普电脑启动"); } }
public class Lenovo extends Computer { @Override public void start() { System.out.println("联想电脑启动"); } }
public class ROG extends Computer{ @Override public void start() { System.out.println("时代的王,世界的狂"); } }
抽象工厂
public abstract class AbstractComputerFactory { public abstract T createComputer(Class clz); }
解释一下里面的代码:
T是一个继承了Computer的泛型类
(Class clz)表示:该方法的参数类型为Class,表示需要传入一个Class对象,
具体工厂
public class ComputerFactory extends AbstractComputerFactory{ @Override public T createComputer(Class clz) { Computer computer = null; String name = clz.getName(); try { computer =(Computer) Class.forName(name).newInstance(); //通过Class对象创建一个新的实例,相当于调用该类的无参构造方法。 } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return (T)computer; } }
- Class.forName(name):根据类名字符串 name 获取对应的Class对象,该方法会抛出ClassNotFoundException异常,如果找不到对应的类。
- newInstance():通过Class对象创建一个新的实例,相当于调用该类的无参构造方法。
然后最后是
客户端调用工厂类
public class User { public static void main(String[] args) { ComputerFactory factory = new ComputerFactory(); HP hp = factory.createComputer(HP.class); Lenovo lenovo = factory.createComputer(Lenovo.class); ROG rog = factory.createComputer(ROG.class); hp.start(); lenovo.start(); rog.start(); } }
最后的输出
惠普电脑启动
联想电脑启动
时代的王,世界的狂
总结一下:
两个都先定义一盒抽象产品类,然后写一个具体产品类继承这个抽象类,重写里面抽象类的方法
接下来就是两者的不一样的地方了:
简单工厂类是直接写,但是工厂方法模式却是先定义一个抽象工厂,然后通过客户端给它传的名称来生产。
4建造者模式(重点)
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public class UserInfo { private String name; private int age; private double height; private double weight; private UserInfo(Builder builder) { this.name = builder.name; this.age = builder.age; this.height = builder.height; this.weight = builder.weight; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } static class Builder { private String name; private int age; private double height; private double weight; public Builder name(String name) { this.name = name; return this; } public Builder age(int age) { this.age = age; return this; } public Builder height(double height) { this.height = height; return this; } public Builder weight(double weight) { this.weight = weight; return this; } public UserInfo build() { return new UserInfo(this); } } }
//创建 UserInfo.Builder builder=new UserInfo.Builder(); UserInfo person=builder .name("张三") .age(18) .height(178.5) .weight(67.4) .build(); //获取UserInfo的属性 Log.e("userinfo","name = "+person.getName());
这是一个比较简单的理解的代码
但是在《进阶之光》里面它把build这个用导演类又封装了一下
其他没啥了
结构型设计
1代理模式
public interface Ishop { void buy(); }
public class HUAWEI implements Ishop{ @Override public void buy() { System.out.println("加油HuaWei,加油China"); } }
public class Honor implements Ishop { private Ishop ishop; public Honor(Ishop ishop) { this.ishop = ishop; } @Override public void buy() { ishop.buy(); } }
public class User { public static void main(String[] args) { HUAWEI huawei = new HUAWEI(); Honor honor = new Honor(huawei); honor.buy(); } }
最后结果输出:
加油HuaWei,加油China
其次是动态代理模式
2动态代理模式(重点)
首先还是先创建一个抽象主题类
抽象主题类
public interface Mobile { void buy(); }
然后创建真实主题类
真实主题类
public class RedMi implements Mobile { @Override public void buy() { System.out.println("干翻小米,红米永存"); } }
真实主题类也就是把抽象主题类implements一下
然后创建动态代理类
动态代理类得实现java的动态代理接口
InvocationHandler
public class Mi implements InvocationHandler { private Object object; public Mi(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object resultv= method.invoke(object,args); if(method.getName().equals("buy")){ System.out.println("小米"); } return resultv; } }
invoke() 方法是由Java虚拟机(JVM)在动态代理对象上调用方法时调用的,该动态代理对象与此 InvocationHandler 实现相关联。该方法接受三个参数:
- proxy - 调用该方法的代理对象
- method - 表示调用的方法的 Method 对象
- args - 一个表示传递给方法的参数的对象数组
invoke() 方法首先使用 method.invoke() 方法在原始对象上调用方法,并将结果存储在名为 resultv 的变量中。然后,它检查调用的方法的名称是否为 “buy”,如果是,则在控制台上打印 “小米”。最后,它返回方法调用的结果。
客户端调用
public class Client { public static void main(String[] args) { //创建Redmi对象 Mobile mobile = new RedMi(); //动态代理 Mi mi = new Mi(mobile); //创建Redmi的ClassLoader ClassLoader loader = mobile.getClass().getClassLoader(); //动态创建代理类 Mobile mobile1 = (Mobile) Proxy.newProxyInstance(loader,new Class[]{Mobile.class},mi); //这行代码使用 Proxy.newProxyInstance() 方法创建一个动态代理对象。该方法的第一个参数是 ClassLoader 对象,用于加载动态代理类的定义;第二个参数是一个 Class 类型的数组,代表动态代理类要实现的接口;第三个参数是一个 InvocationHandler 对象,用于拦截对象的方法调用。该方法返回一个实现了指定接口的动态代理对象,并将其转换为 Mobile 类型的变量 mobile1。 mobile1.buy(); } }
动态代理的 ClassLoader 用于加载代理类的定义,使得代理对象能够在运行时动态生成,并可以在代理对象调用方法时将方法调用转发到 InvocationHandler 实现类中。
3装饰模式
抽象组件
public abstract class Mobile { public abstract void whichPhone(); }
组件实现具体类
public class Honor extends Mobile{ @Override public void whichPhone() { System.out.println("是荣耀手机"); } }
抽象装饰者
public abstract class Master extends Mobile { public Master(Mobile father) { this.father = father; } private Mobile father; @Override public void whichPhone() { father.whichPhone(); } }
抽象装饰者实现具体类
public class HuaWei extends Master{ public HuaWei(Mobile father) { super(father); } public void isFather(){ System.out.println("是华为手机"); } @Override public void whichPhone() { super.whichPhone(); isFather(); } }
客户端调用
public class Client { public static void main(String[] args) { Honor honor = new Honor(); HuaWei huaWei = new HuaWei(honor); huaWei.whichPhone(); } }
4外观模式
子系统类
public class interior { public void child(){ System.out.println("荣耀手机"); } }
外观类
public class appreanceClass { private interior interior; public appreanceClass(appearance.interior interior) { this.interior = interior; } public void father(){ System.out.println("华为"); interior.child(); } }
客户端调用
public class Client { public static void main(String[] args) { interior interior = new interior(); appreanceClass appreanceClass = new appreanceClass(interior); appreanceClass.father(); } }
5享元模式(重点)
抽象享元角色
public interface rool { public void showGoods(String name); }
具体享元角色
public class Goods implements rool { public Goods(String name) { this.name = name; } private String name; private String version; @Override public void showGoods(String version) { if(version.equals("32G")){ System.out.println("价格为5199"); } else if(version.equals("128G")){ System.out.println("价格为5999"); } } }
享元工厂
public class Factory { private static Mappool = new HashMap(); public static Goods getGoods(String name){ if(pool.containsKey(name)){ System.out.println("使用缓存,key为:"+name); return pool.get(name); } else{ Goods goods = new Goods(name); pool.put(name,goods); System.out.println("创建商品,key为:"+name); return goods; } } }
客户端调用
public class Client { public static void main(String[] args) { Goods goods = Factory.getGoods("iphone7"); goods.showGoods("32G"); Goods goods1 = Factory.getGoods("iphone7"); goods1.showGoods("128G"); Goods goods2 = Factory.getGoods("iphone7"); goods2.showGoods("32G"); Goods goods3 = Factory.getGoods("iphone8"); goods3.showGoods("128G"); } }
享元模式这个和其他几个差距还是挺大的,其他的感觉一直在封装,然后调用。而享元模式的核心思路就是,开辟一个新的空间用于存储(这里用的是HashMap存储),如果原来有这些东西,那就从HashMap里面找,不用的话就new一个新的对象,然后再把它存到HashMap里
行为型设计模式
1.观察者模式(重点)
抽象观察者
public interface Observer { public void update(String message); }
具体观察者(是谁)
public class WeixinUser implements Observer { private String name; public WeixinUser(String name){ this.name = name; } @Override public void update(String message) { System.out.println("name"+name+"message"+message); } }
抽象被观察者
public interface Subject { public void attach(Observer observer); public void detach(Observer observer); public void notify(String message); }
具体被观察者(要做的事情)
public class SubscriptionSubject implements Subject{ private ListweixinUserList = new ArrayList(); @Override public void attach(Observer observer) { weixinUserList.add(observer); } @Override public void detach(Observer observer) { weixinUserList.remove(observer); } @Override public void notify(String message) { for (Observer observer : weixinUserList) { observer.update(message); } } }
客户端
public class Client { public static void main(String[] args) { SubscriptionSubject subscriptionSubject = new SubscriptionSubject(); WeixinUser user = new WeixinUser("小米1"); WeixinUser user1 = new WeixinUser("小米2"); WeixinUser user2 = new WeixinUser("小米3"); subscriptionSubject.attach(user); subscriptionSubject.attach(user1); subscriptionSubject.attach(user2); subscriptionSubject.notify("1999"); subscriptionSubject.detach(user2); subscriptionSubject.notify("2499"); } }
name小米1message1999
name小米2message1999
name小米3message1999
name小米1message2499
name小米2message2499
简单总结一下
单例模式没啥好说的就是无论调用多少次那个类从始至终都是同一个对象。
工厂方法模式使用场景
工厂方法模式可以用于需要创建多个具有共同接口或抽象类的对象,需要在运行时动态地创建对象,需要对创建对象的过程进行自定义或扩展,以及需要隐藏对象的创建过程的场景。
建造者模式的使用场景
建造者模式可以用于需要创建复杂的对象,需要创建多个具有相似属性的对象,需要创建一个构造函数参数较为复杂的对象,以及需要创建一个构造函数参数可能会变化的对象的场景。
动态代理模式的使用场景
动态代理模式可以用于需要对目标对象进行访问控制、增强或扩展,需要对目标对象进行远程调用,以及需要在无法修改目标对象源代码的情况下对目标对象进行扩展的场景。
享元模式的使用场景
享元模式可以用于需要创建大量相似的对象、需要缓存对象以提高性能、需要对对象进行池化管理,以及需要在多个对象之间共享状态的场景。
观察者模式的使用场景
观察者模式可以用于需要实现消息推送、事件处理、异步消息处理、状态同步等功能的场景。它可以将发布者和订阅者解耦,提高系统的灵活性和可扩展性。
还没有评论,来说两句吧...