此文基于之前看【Spring核心原理】整理的学习笔记,建议手撸一遍代码加深印象。
简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建哪一种产品类的实例,但它不属于GoF的23种设计模式.
通过反射的方式生成对象
缺点: 工厂类的职责相对过重,不易于扩展过于复杂的逻辑代码
public interface Course {
void record();
}
public class JavaCourse implements Course {
@Override
public void record() {
System.out.println("java课程");
}
}
public class CourseFactory {
public static Course getCourse(Class<? extends Course> clas) {
try {
if (null != clas) {
return clas.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
Course course=CourseFactory.getCourse(JavaCourse.class);
course.record();
}
}
工厂方法模式(Fatory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法模式让类的实例化推迟到子类进行,在工厂方法模式中用户只需要关心所需产品对应的工厂,无需关心创建细节,而加入新的产品也符合开闭原则
public interface Course {
void record();
}
public class JavaCourse implements Course {
@Override
public void record() {
System.out.println("java课程");
}
}
public interface CourseFactory {
Course getCourse();
}
public class JavaCourseFacotry implements CourseFactory {
@Override
public Course getCourse() {
return new JavaCourse();
}
public static void main(String[] args) {
Course course = new JavaCourseFacotry().getCourse();
course.record();
}
}
抽象工厂模式(Abstract Factory Pattern)是指提供一个创建一系列相关或者相关依赖对象的接口,无需指定他们的具体实现类.
spring中应用得最广泛的一种设计模式
public interface Note {
void edit();
}
public interface Video {
void record();
}
public class JavaNote implements Note {
@Override
public void edit() {
System.out.println("编写Java笔记");
}
}
public class JavaVideo implements Video {
@Override
public void record() {
System.out.println("录制Java视频");
}
}
public interface CourseFactory {
Note getNote();
Video getVideo();
}
public class JavaCourseFactory implements CourseFactory {
@Override
public Note getNote() {
return new JavaNote();
}
@Override
public Video getVideo() {
return new JavaVideo();
}
public static void main(String[] args) {
CourseFactory courseFactory = new JavaCourseFactory();
courseFactory.getNote().edit();
courseFactory.getVideo().record();
}
}
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,单例模式是创建型模式,Spring框架中ApplicationContext,数据库的连接池等也是单例形式
饿汉单例模式在类加载的时候就立即初始化,并且创建对象了,它绝对线程安全,在线程还没出现之前就实例化了,不存在访问安全问题
缺点: 类加载时候就初始化,不管用与不用都占用空间,浪费内存
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() { }
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
懒汉模式的特点是: 被外部类调用的时候内部类才会初始化
下面采用内部类方式实现
public class LazySingleton {
private LazySingleton() { }
public static LazySingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
}
2.3.1反射破坏
public class Main {
public static void main(String[] args) throws Exception {
LazySingleton s1 = LazySingleton.getInstance();
//无聊的情况下,进行破坏
Constructor<LazySingleton> constructor = LazySingleton.class.getDeclaredConstructor();
//强制访问
constructor.setAccessible(true);
//暴力初始化
Object s2 = constructor.newInstance();
System.out.println(s1 == s2);
}
}
改善LazySingleton防止反射破坏
public class LazySingleton {
private LazySingleton() {
if(LazyHolder.INSTANCE!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
public static LazySingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
}
2.3.2序列化破坏单例
public class SeriableSingleton implements Serializable {
private static final SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton() { }
public static SeriableSingleton getInstance() {
return INSTANCE;
}
}
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
添加readResolve()方法即修复此问题
public class SeriableSingleton implements Serializable {
private static final SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton() { }
public static SeriableSingleton getInstance() {
return INSTANCE;
}
private Object readResolve() {
return INSTANCE;
}
}
ObjectInputStream
对象的序列化过程通过ObjectOutputStream和ObjectInputputStream来实现的,那么带着刚刚的问题,分析一下ObjectInputputStream 的readObject 方法执行情况到底是怎样的。
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//此处省略部分代码
Object obj;
try {
//这里创建的这个obj对象,就是本方法要返回的对象,也可以暂时理解为是ObjectInputStream的readObject返回的对象。
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
//此处省略部分代码
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
isInstantiable:如果一个serializable/externalizable的类可以在运行时被实例化,那么该方法就返回true。
*
desc.newInstance:该方法通过反射的方式调用无参构造方法新建一个对象。
*
hasReadResolveMethod:如果实现了serializable 或者 externalizable接口的类中包含readResolve则返回true
*
invokeReadResolve:通过反射的方式调用要被反序列化的类的readResolve方法。
所以,原理也就清楚了,主要在Singleton中定义readResolve方法,并在该方法中指定要返回的对象的生成策略,就可以防止单例被破坏。
为什么序列化可以破坏单例了?
答:序列化会通过反射调用无参数的构造方法创建一个新的对象。
注册式单例模式又称登记式单例模式,都是将每一个实例登记到某个地方,使用唯一的标识来获取实例.注册式单例模式有两种:一种为枚举式单例模式,另一种为容器式单例模式
public enum EnumSingleton {
INSTANCE;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
public static class User {
}
public static void main(String[] args) {
EnumSingleton s1 = EnumSingleton.getInstance();
EnumSingleton s2 = EnumSingleton.getInstance();
//true
System.out.println(s1 == s2);
}
}
测试反射破坏枚举单例模式
EnumSingleton s1 = EnumSingleton.getInstance();
//无聊的情况下,进行破坏
Constructor<EnumSingleton> constructor = EnumSingleton.class.getDeclaredConstructor();
//强制访问
constructor.setAccessible(true);
//暴力初始化
Object s2 = constructor.newInstance();
System.out.println(s1 == s2);
下面异常显示没有找到构造方法,枚举类只有protected类型的构造方法
测试序列化破坏枚举单例模式
EnumSingleton s1 = null;
EnumSingleton s2 = EnumSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("EnumSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("EnumSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (EnumSingleton) ois.readObject();
ois.close();
//输出true
System.out.println(s1==s2);
} catch (Exception e) {
e.printStackTrace();
}
查看ObjectInputStream的readObject0方法
readEnum方法实现如下
private Enum<?> readEnum(boolean unshared) throws IOException {
if (bin.readByte() != TC_ENUM) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
if (!desc.isEnum()) {
throw new InvalidClassException("non-enum class: " + desc);
}
int enumHandle = handles.assign(unshared ? unsharedMarker : null);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(enumHandle, resolveEx);
}
String name = readString(false);
Enum<?> result = null;
Class<?> cl = desc.forClass();
if (cl != null) {
try {
@SuppressWarnings("unchecked")
Enum<?> en = Enum.valueOf((Class)cl, name);
result = en;
} catch (IllegalArgumentException ex) {
throw (IOException) new InvalidObjectException(
"enum constant " + name + " does not exist in " +
cl).initCause(ex);
}
if (!unshared) {
handles.setObject(enumHandle, result);
}
}
handles.finish(enumHandle);
passHandle = enumHandle;
return result;
}
枚举类型其实通过类名和类对象类找到一个唯一的枚举对象,因此,枚举对象不可能被加载器加载多次
spring中的AbstractAutowireCapableBeanFactory
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<>();
public static Object getBean(String classsName) {
synchronized (ioc) {
if (!ioc.containsKey(classsName)) {
Object obj = null;
try {
obj = Class.forName(classsName).newInstance();
ioc.put(classsName, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(classsName);
}
}
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(()->{
System.out.println(getBean(User.class.getName()));
}).start();
}
}
}
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过复制这些原始创建新的对象.
在Spring中,原型模式应用得非常广泛,例如scope=“prototype”.
浅克隆只复制了属性的值,但是没有赋值引用对象
public interface Prototype {
Prototype clone();
}
public class BeanUtil {
private BeanUtil(){ }
public static Prototype clone(Prototype prototype) {
return prototype.clone();
}
}
public class User implements Prototype {
private String username;
private List<String> hobbyList;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getHobbyList() {
return hobbyList;
}
public void setHobbyList(List<String> hobbyList) {
this.hobbyList = hobbyList;
}
@Override
public Prototype clone() {
User user = new User();
user.username = this.username;
user.hobbyList = this.hobbyList;
return user;
}
}
public static void main(String[] args) {
User user = new User();
user.setUsername("test");
user.setHobbyList(Arrays.asList("撸铁"));
User user1 = (User) BeanUtil.clone(user);
user1.getHobbyList().set(0,"写博客");
//输出 写博客,true
System.out.println(user.getHobbyList().get(0));
}
由于List是引用类型,所以user和user1的hobbyList是一个对象
深克隆在克隆属性的同时,通过序列化反射生成了一个新的引用地址
public class User implements Cloneable, Serializable {
private String username;
private List<String> hobbyList;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getHobbyList() {
return hobbyList;
}
public void setHobbyList(List<String> hobbyList) {
this.hobbyList = hobbyList;
}
@Override
public Object clone() throws CloneNotSupportedException{
//Cloneable.clone接口默认是浅克隆
//return super.clone();
return this.deepClone();
}
public Object deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
User copy = (User) ois.readObject();
return copy;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public static void main(String[] args) {
User user = new User();
user.setUsername("test");
user.setHobbyList(Arrays.asList("撸铁"));
User user1 =(User) user.clone();
user1.getHobbyList().set(0, "写博客");
//输出 撸铁 false
System.out.println(user.getHobbyList().get(0));
System.out.println(user.getHobbyList() == user1.getHobbyList());
}
代理模式(Proxy Pattern)给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
public interface BuyHouse {
void buyHosue();
}
public class BuyHouseImpl implements BuyHouse{
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("买房前准备钱");
buyHouse.buyHosue();
System.out.println("和中介签订合同");
}
}
public class Main {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("和中介签订合同");
return result;
}
}
public class Main {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{ BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
注意Proxy.newProxyInstance()方法接受三个参数:
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
public class BuyHouseTwo {
public void buyHosue() {
System.out.println("我要买房");
}
}
public class CglibProxy implements MethodInterceptor {
public Object getProxy(Class clas) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clas);
enhancer.setCallback(this);
return enhancer.create();
}
/** * @param obj 表示要进行增强的对象 * @param method 表示拦截的方法 * @param args 数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double * @param methodProxy 表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用 * @return 执行结果 */
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前准备");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("买房后装修");
return result;
}
}
public class Main {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
BuyHouseTwo proxy = (BuyHouseTwo) cglibProxy.getProxy(BuyHouseTwo.class);
proxy .buyHosue();
}
}
CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
JDK Proxy的优势:
最小化依赖关系、代码实现简单、简化开发和维护、JDK原生支持,比CGLIB更加可靠,随JDK版本平滑升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。
CGLIB的优势:
无需实现接口,达到代理类无侵入,只操作关心的类,而不必为其他相关类增加工作量。高性能。
ProxyFactoryBean核心方法getObject()
public Object getObject() throws BeansException {
this.initializeAdvisorChain();
if (this.isSingleton()) {
return this.getSingletonInstance();
} else {
if (this.targetName == null) {
this.logger.warn("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
}
return this.newPrototypeInstance();
}
}
在spring的配置中如果不做任何处理设置,那么spring代理的类都是单例对象,如果修改scope为prototype,则每次创建一个新的对象
spring bean的scope属性
Spring利用动态代理实现AOP时有两个非常重要的类,JdkDynamicAopProxy类和CglibAopProxy类
Spring中的代理选择原则
优点
缺点
委派模式(Delegate Pattern)不属于GoF 23种设计模式.在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承,它还使我们可以模拟mixin。
委派模式和代理模式很像,可以看作是一种特殊情况下的静态全局代理,但是代理模式注重过程,委托模式注重结果, web开发中常用的DispatcherServlet就用到了委托模式.在Spring源码中,以Delegate结尾的地方都实现了委派模式
本文以找黄牛买票为例
/** * @description 票类接口 **/
public interface Ticket {
void doing();
}
public class TrainTicket implements Ticket {
@Override
public void doing() {
System.out.println("买到南昌的飞机票");
}
}
public class ConcertTicket implements Ticket {
@Override
public void doing() {
System.out.println("买许嵩的演唱会票"); //黄牛
}
}
public class Cattle {
public void doing(Ticket ticket) {
System.out.println("我是黄牛");
ticket.doing();
System.out.println("买票结束");
}
}
public class Main {
public static void main(String[] args) {
Cattle cattle=new Cattle();
cattle.doing(new ConcertTicket());
cattle.doing(new TrainTicket());
}
}
策略模式(Strategy Pattern)是指定义了算法家族并分别封装起来,让它们可以相互替换,此模式使算法的变化不会影响使用算法的用户.
下面以网购买完东西,结账时候选择支付方式为例
public abstract class Payment {
//支付类型
public abstract String getName();
//查询余额
protected abstract double queryBalance(String uid);
//扣款支付
public PayState pay(String uid,double amount){
if(queryBalance(uid)<amount){
return new PayState(500,"支付失败","余额不足");
}
return new PayState(200,"支付成功","交易金额:"+amount);
}
}
public class PayState {
private int code;
private Object data;
private String msg;
public PayState(int code,String msg,Object data){
this.code=code;
this.msg=msg;
this.data=data;
}
@Override
public String toString(){
return "支付状态:["+code+"],"+msg+",交易详情:"+data;
}
}
public class AliPay extends Payment {
@Override
public String getName() {
return "支付宝";
}
@Override
protected double queryBalance(String uid) {
return 900;
}
@Override
public PayState pay(String uid, double amount) {
return super.pay(uid, amount);
}
}
public class WechatPay extends Payment {
@Override
public String getName() {
return "微信支付";
}
@Override
protected double queryBalance(String uid) {
return 257;
}
@Override
public PayState pay(String uid, double amount) {
return super.pay(uid, amount);
}
}
public class PayStrategy {
public static final String ALI_PAY = "AliPay";
public static final String WECHAT_PAY = "WechatPay";
public static final String DEFAULT_PAY = ALI_PAY;
private static Map<String, Payment> payStrategy = new HashMap<String, Payment>();
static {
payStrategy.put(ALI_PAY, new AliPay());
payStrategy.put(WECHAT_PAY, new WechatPay());
}
/** * 根据key选择对应的策略 */
public static Payment get(String payKey) {
if (!payStrategy.containsKey(payKey)) {
return payStrategy.get(DEFAULT_PAY);
}
return payStrategy.get(payKey);
}
}
public class Order {
private String uid;
private String orderId;
private double amount;
public Order(String uid, String orderId, double amount) {
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
public PayState pay(String payKey) {
Payment payment = PayStrategy.get(payKey);
System.out.println("欢迎使用" + payment.getName());
System.out.println("本次校次金额为:" + amount + ",开始扣款.....");
return payment.pay(uid, amount);
}
}
public class Main {
public static void main(String[] args) {
Order order=new Order("1","20210118",389);
//选择策略支付
System.out.println(order.pay(PayStrategy.ALI_PAY));
}
}
策略模式源码中应用场景
1.策略模式在JDK中源码的体验
比较容器-Comparator接口,大家常用的compare()方法就是一个策略模式的抽象实现
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
2.在Spring中的应用
Spring在初始化也采用了策略模式,即不同类型的类采用不同的初始化策略,
InstantiationStrategy接口
public interface InstantiationStrategy {
Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3) throws BeansException;
Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3, Constructor<?> var4, Object... var5) throws BeansException;
Object instantiate(RootBeanDefinition var1, @Nullable String var2, BeanFactory var3, @Nullable Object var4, Method var5, Object... var6) throws BeansException;
}
顶层的策略抽象非常简单,它下面有两种策略: SimpleInstantiationStrategy
CglibSubclassingInstantiationStrategy,
public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
}
CglibSubclassingInstantiationStrategy继承自SimpleInstantiationStrategy类,说明实际应用中多种策略可以继承使用.
策略模式的优缺点:
优点:策略符合开闭原则,策略模式可避免使用多重条件语句,如if…else语句,switch语句,使用策略模式可以提供算法的保密性和安全性
缺点:客户端必须知道所有策略,并且自行决定使用哪个策略
模板模式又叫模板方法模式(Template Method Pattern),是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现.模板模式使得子类可以在不变算法的前提下,重新定义算法的某些步骤,属于行为型设计模式,模板模式使用与以下场景
我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类,它们重写了抽象类的方法。
TemplatePatternDemo,我们的演示类使用 Game 来演示模板模式的用法。
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
public class Main {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
模式模式的优点
适配器模式(Adapter Pattern)是指将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
使用场景如下
此处以重构第三方登录适配为例
老系统一般刚开始只有密码登录的系统,随着社会的进步,单纯的密码登录已经满足不了广大网友了,大部分系统已经支持多种登录方式,如QQ登录,微信登录,手机登录,微博登录等.
//登录接口
public class SiginService {
/** * 注册 */
public ResultMsg regist(String username,String password){
return new ResultMsg(200,"注册成功",new User());
}
/** * 登录 */
public ResultMsg login(String username,String password){
return null;
}
}
//返回结果封装
public class ResultMsg {
private int code;
private String msg;
private Object data;
public ResultMsg(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
/** * @author Dominick Li * @CreateTime 2021/1/18 22:29 * @description 定义登录适配器规范 **/
public interface LoginAdapter {
boolean support(Object adapter);
ResultMsg login(Object [] param, Object adapter);
}
//QQ登录
public class LoginForQQAdapter implements LoginAdapter {
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginForQQAdapter;
}
@Override
public ResultMsg login(Object[] param, Object adapter) {
System.out.println("QQ登录");
return null;
}
}
//手机号登录
public class LoginForTelAdapter implements LoginAdapter {
@Override
public boolean support(Object adapter) {
return adapter instanceof LoginForTelAdapter;
}
@Override
public ResultMsg login(Object[] param, Object adapter) {
System.out.println("手机号登录");
return null;
}
}
/** * @author Dominick Li * @CreateTime 2021/1/18 22:25 * @description 第三方登录兼容接口 **/
public interface PassportForThird {
/** * qq登录 */
ResultMsg loginForQQ(String id);
/** * 手机号登录 */
ResultMsg loginForTelphone(String telphone,String code);
/** * 注册后自动登录 */
ResultMsg loginForRegist(String username,String password);
}
/** * @description 第三方登录兼容接口 **/
public interface PassportForThird {
/** * qq登录 */
ResultMsg loginForQQ(String id);
/** * 手机号登录 */
ResultMsg loginForTelphone(String telphone,String code);
/** * 注册后自动登录 */
ResultMsg loginForRegist(String username,String password);
}
public class PassportForThirdAdapter extends SiginService implements PassportForThird {
@Override
public ResultMsg loginForQQ(String id) {
return processLogin(new Object[]{ id}, LoginForQQAdapter.class);
}
@Override
public ResultMsg loginForTelphone(String telphone, String code) {
return processLogin(new Object[]{ telphone,code}, LoginForTelAdapter.class);
}
@Override
public ResultMsg loginForRegist(String username, String password) {
super.regist(username,password);
return super.login(username,password);
}
/** * 简单工厂模式 */
private ResultMsg processLogin(Object [] param, Class<? extends LoginAdapter> clas) {
try {
LoginAdapter adapter = clas.newInstance();
if (adapter.support(adapter)) {
return adapter.login(param, adapter);
}
return null;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class Main {
public static void main(String[] args) {
PassportForThird passportForThird=new PassportForThirdAdapter();
passportForThird.loginForTelphone("12345","123");
}
}
适配器模式主要解决的是功能兼容问题
适配器模式在Spring源码中的使用
public interface AdvisorAdapter {
// 判断此适配器是否支持特定的Advice
boolean supportsAdvice(Advice var1);
//将一个Advisor适配成MethodInterceptor
MethodInterceptor getInterceptor(Advisor var1);
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() {
}
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
Spring会根据不同的AOP配置来使用对应的Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
//判断是否兼容
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
适配器模式的优缺点
优点:
缺点:
装饰器模式(Decorator Pattern)是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的方案(扩展原有对象的功能),属于结构性模式,装饰者模式适用于以下场景
此处以卖煎饼为示例
public class Battercake {
protected String product() {
return "煎饼";
}
protected int getPrice() {
return 5;
}
@Override
public String toString() {
return product() + ",总价格:" + getPrice();
}
}
//加一个鸡蛋
public class BattercakeWithEgg extends Battercake {
@Override
protected String product() {
return super.product()+"加1个鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice()+1;
}
}
//加一个鸡蛋和一个香肠
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
@Override
protected String product() {
return super.product()+"加1个香肠";
}
@Override
protected int getPrice() {
return super.getPrice()+2;
}
}
public class Main {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println(battercake);
Battercake battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg);
Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
System.out.println(battercakeWithEggAndSausage);
}
}
装饰器模式和适配器模式对比
装饰器模式和适配器模式都是(Wraooer Pattern),装饰器模式也是一种特殊的代理模式,二者对比如下
列名 装饰器模式 适配器模式 形式 是一种特别的适配器模式 没有层级关系,装饰器模式有 定义 装饰者和被装饰者实现同一个接口,主要目的是扩展之后依旧保留OOP关系 适配器和被适配者没有必然的联系,通常采用继承或者代理的形式进行包装 关系 满足 is-a的关系 满足has-a的关系 功能 注重覆盖,扩展 注重兼容,转换 设计 前置考虑 后置考虑
装饰器模式在源码中的应用
在JDK中体现最多的就是I/O相关的类,如BufferedReader,InputStream,OutputStream
如下图,FilterInputStream被3个子类引用了.
在Spring中的TransactionAwareCacheDecotator类中有被使用,这个类主要用于处理事务缓存的,源码如下
public class TransactionAwareCacheDecorator implements Cache {
private final Cache targetCache;
public TransactionAwareCacheDecorator(Cache targetCache) {
Assert.notNull(targetCache, "Target Cache must not be null");
this.targetCache = targetCache;
}
}
TransactionAwareCacheDecorator就是对Cache的一个包装.
装饰器模式的优缺点
优点:
缺点:
观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有的依赖者(观察者)都会受到通知并更新,属于行为者模式,观察者模式有时候也叫发布订阅模式,例如我们生活中的邮件通知,桌面程序的事件响应等.
//监听器的一种包装,标注事件源格式的定义
public class Event {
//事件源,事件是由谁发起的,保存起来
private Object source;
//事件触发,要通知谁
private Object target;
//事件触发,要做什么动作,回调
private Method callback;
//事件的名称,触发的是什么事件
private String trigger;
//事件触发的时间
private long time;
public Event(Object target, Method callback) {
this.target = target;
this.callback = callback;
}
public Event setSource(Object source) {
this.source = source;
return this;
}
public Event setTrigger(String trigger) {
this.trigger = trigger;
return this;
}
public Event setTime(long time) {
this.time = time;
return this;
}
}
//监听器,它就是观察者的桥梁
public class EventListenter {
protected Map<String, Event> events = new HashMap<>();
//通过事件名称和一个目标对象来触发事件
public void addLisenter(String eventType, Object target) {
try {
this.addLisenter(eventType, target, target.getClass().getMethod(eventType, Event.class));
} catch (Exception e) {
e.printStackTrace();
}
}
public void addLisenter(String eventType, Object target, Method callback) {
//注册事件
events.put(eventType, new Event(target, callback));
}
//触发
private void trigger(Event event) {
event.setSource(this);
event.setTime(System.currentTimeMillis());
try {
//发起回调
if (event.getCallback() != null) {
//用反射调用它的回调函数
event.getCallback().invoke(event.getTarget(), event);
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected void trigger(String trigger){
if(!this.events.containsKey(trigger)){ return;}
trigger(this.events.get(trigger).setTrigger(trigger));
}
}
//定义事件类型
public interface EventType {
String CLICK="click";
}
/** * @author Dominick Li * @CreateTime 2021/1/19 22:37 * @description 回调函数类 **/
public class MouseEventCallback {
public void click(Event e){
System.out.println("==============触发鼠标点击事件===============\n"+e);
}
}
//被观察者
public class Mouse extends EventListenter {
public void click(){
System.out.println("调用单机事件");
this.trigger(EventType.CLICK);
}
}
public class Main {
public static void main(String[] args) {
MouseEventCallback callback=new MouseEventCallback();
//注册事件
Mouse mouse=new Mouse();
mouse.addLisenter(EventType.CLICK,callback);
//调用方法
mouse.click();
}
}
观察者模式在源码中的应用
Spring中的ContextLoaderListener类实现了ServletContextListener接口,ServletContextListener又继承了EventListener接口
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
public interface ServletContextListener extends EventListener {
default void contextInitialized(ServletContextEvent sce) {
}
default void contextDestroyed(ServletContextEvent sce) {
}
}
public interface EventListener {
}
基于Guava API轻松实现观察者模式
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
public class GuavaEvent {
@Subscribe
public void subscribe(String str){
System.out.println("执行subscribe方法,传入的参数是"+str);
}
}
public class Main {
public static void main(String[] args) {
EventBus eventBus=new EventBus();
GuavaEvent guavaEvent=new GuavaEvent();
eventBus.register(guavaEvent);
eventBus.post("我喜欢你");
}
}
观察者模式的优缺点
优点
缺点
分类 设计模式 创建型 工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式 结构型 适配器模式,桥接模式,组合模式,装饰者模式,门面模式,享元模式,代理模式 行为型 解释器模式,模板方法模式(模板模式),责任链模式,命令模式,迭代器模式,调节者模式,备忘录模式,观察者模式,状态模式,策略模式,访问者模式.
设计模式直接的关联关系
模式组合 描述 单例模式和工厂模式 在实际使用中,会把工厂类设置为单例模式 策略模式和工厂模式 工厂模式包含工厂方法模式和抽象工厂模式,是创建型模式,策略模式属于行为者模式,工厂模式的主要目的是封装好创建逻辑,策略模式接受工厂创建好的对象,从而实现不同的行为 策略模式和委派模式 策略模式是委派模式内部的一种实现方式,测试模式关注结果是否能相互替代,委派模式更关注分发和调度的过程 模板方法模式和工厂方法模式 工厂方法模式是模板方法模式的一种特殊实现,对于工厂方法模式的create()方法而言,工厂方法模式相当于只有一个步骤的模板方法模式,这个步骤交给子类实现。 模式方法模式和策略模式 模板方法模式和策略模式都有封装算法,策略模式使不同算法可以相互替换,且不影响客户端应用,使用模板方法模式针对定义一个算法的流程,将一些有细微差异的部分交给子类实现,模板方法模式不能改变算法流程,策略模式可以 装饰者模式和代理模式 装饰者模式的关注点在于给对象动态添加方法,而代理模式根加注重控制对象的访问,代理模式通常会在代理类中创建被代理类对象的实例,二装饰者模式通常会把装饰者作为构造参数 装饰者模式和适配器模式 装饰者模式和适配器模式都属于包装模式 (Wrapper Pattern),装饰者模式可以试下与被装饰者相同的接口,或者继承被装饰者作为它的子类,而适配器和被适配器着都可以实现不同的接口. 适配器模式和静态代理模式 适配器模式可以结合静态代理模式来实现,保存被适配对象的引用 适配器模式和策略模式 在业务复杂的情况下可以用策略模式优化器适配器模式
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/ming19951224/article/details/119361300
内容来源于网络,如有侵权,请联系作者删除!