设计模式的原则:
1、开闭原则 对扩展开放,对修改关闭。
2、依赖倒置原则 通过抽象使各个类或者模块不相互影响,实现松耦合。
3、单一职责原则 一个类、接口、方法只做一件事。
4、接口隔离原则 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。
5、迪米特法则 又叫最少知道原则,一个类对其所依赖的类知道得越少越好。
6、里氏替换原则 子类可以扩展父类的功能但不能改变父类原有的功能。
7、合成复用原则 尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。
代码地址: https://github.com/ZhangYDevelop/java-design-pattern.git
一、工厂模式
package com.zy.java.design.patterns.factory;
/**
* @AUTHOR zhangy
* 2020-10-11 20:22
* 工厂模式(simple factory pattern) ,列子:Spring 中的BeanFactory
*/
public class FactoryPattern {
/**
* 生产汽车的工厂
*/
private static class CarFactory {
public static Car getCarInstance(String type) {
if (ICar.BEN_CHI.equals(type)) {
return new BenChiCar().createCar();
}
if (ICar.DA_ZHONG.equals(type)) {
return new DaZhongCar().createCar();
}
return null;
}
}
/**
* 制造商
*/
private static class BenChiCar implements ICar {
public Car createCar() {
System.out.println("生产奔驰车。。。");
return new Car();
}
}
/**
* 制造商
*/
private static class DaZhongCar implements ICar {
public Car createCar() {
System.out.println("生产大众车...");
return new Car();
}
}
/**
* 对生产车辆抽象
*/
private interface ICar {
String BEN_CHI = "BEN_CHI";
String DA_ZHONG = "DA_ZHONG";
Car createCar();
}
/**
* 物质抽象
*/
private static class Car {
private String whell;
private String engine;
}
public static void main(String[] args) {
CarFactory.getCarInstance(ICar.DA_ZHONG);
}
}
二、单列模式(提供一个全局访问点)
public class Singleton {
// volatile 修饰,线程可见
private volatile static Singleton singleton= null;
// 私有化构造方案
private Singleton() {
if (null != singleton) {
throw new RuntimeException("单列不允许多实例");
// //防止以下流氓方法
// Class<?> clazz = Singleton.class;
// try {
// // 通过反射获取构造方法
// Constructor construct = clazz.getDeclaredConstructor(null);
// // 强制访问
// construct.setAccessible(true);
// // 实例化对象
// Object o1 = construct.newInstance();
// Object o2 = construct.newInstance();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
/**
* 双重检查
* @return
*/
public static final Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
/**
* 通过内部类获取
* @return
*/
public static final Singleton getInstanceExt() {
//在返回结果以前,一定会先加载内部类
return SingleHolder.singleton;
}
/**
* 防止序列化破快单列
* @return
*/
private Object readResolve(){
return singleton;
}
private static class SingleHolder {
private static final Singleton singleton = new Singleton();
}
}
三、原型模式(Prototype Pattern)
原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
应用场景:
1、类初始化消耗资源较多。
2、new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
3、构造函数比较复杂。
4、循环体中生产大量对象时。
在Spring 中,原型模式应用得非常广泛。例如scope=“prototype”,在我们经常用
的JSON.parseObject()也是一种原型模式
/**
* 原型模式(Prototype Pattern),相当于拷贝
*/
public class PrototypePattern {
public interface ProtoType {
ProtoType clone();
ProtoType deepClone();
}
public static class PrototypeA implements ProtoType {
int age;
String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 浅克隆
public ProtoType clone() {
PrototypeA prototypeA = new PrototypeA();
prototypeA.setAge(this.age);
prototypeA.setName(this.name);
return prototypeA;
}
// 深克隆
public ProtoType 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);
ProtoType copy = (ProtoType)ois.readObject();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
public static void main(String[] args) {
PrototypeA prototypeA = new PrototypeA();
prototypeA.setName("zhangsan");
prototypeA.setAge(12);
ProtoType protoType = prototypeA.clone();
}
}
四、代理模式(Proxy Pattern)
该模式为其他对象提供一种代理, 以控制对这个对象的访问。
Jdk动态代理
import sun.misc.ProxyGenerator;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Jdk 动态代理
*/
public class JdkProxy implements InvocationHandler {
/**
* 被代理类必须实现接口
*/
Object target;
public Object getInstance(Object object) {
this.target = object;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object retObj = method.invoke(target, args);
after();
return retObj;
}
/**
* 可以模拟spring的事务开启
*/
private void after() {
System.out.println("开启事务。。。");
}
/**
* 可以模拟spring的事务提交
*/
private void before() {
System.out.println("提交事务。。。。");
}
public interface CrudHandler{
Object addObject(Object o);
void delete(String id);
}
public static class CrudHandlerImpl implements CrudHandler {
@Override
public Object addObject(Object o) {
System.out.println("向数据库添加一条数据");
return null;
}
@Override
public void delete(String id) {
System.out.println("删除数据库一条数据, id=" + id );
}
}
public static void main(String[] args) {
CrudHandler crudHandler = (CrudHandler)new JdkProxy().getInstance(new CrudHandlerImpl());
crudHandler.delete("dfdfdfdfdf");
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{CrudHandler.class});
try {
FileOutputStream fos = new FileOutputStream("C://zhangyu//$Proxy0.class");
fos.write(bytes);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
我们读取代理类$Proxy0并写到磁盘,通多 jad 命令来反编译文件(jad $Proxy0.class proxy0.jad),就可以很清楚的看到jdk动态代理的原理,也很容易理解为什么jdk代理为什么只能代理接口。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements JdkProxy.CrudHandler
{
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
###########沈略部分代码########
public final Object addObject(Object obj)
{
try
{
return (Object)super.h.invoke(this, m4, new Object[] {
obj
});
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void delete(String s)
{
try
{
super.h.invoke(this, m3, new Object[] {
s
});
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
###########沈略部分代码########
}
Cglib代理,它是通过动态继承目标对象 实现的动态代理
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib 动态代理
*/
public class CglibProxy implements MethodInterceptor{
public Object getCglibProxyInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
//要把哪个设置为即将生成的新类父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object retObj = methodProxy.invokeSuper(o,objects);
after();
return retObj;
}
/**
* 可以模拟spring的事务开启
*/
private void after() {
System.out.println("开启事务。。。");
}
/**
* 可以模拟spring的事务提交
*/
private void before() {
System.out.println("调校事务。。。。");
}
public static void main(String[] args) {
Hanlder handler = (Hanlder)new CglibProxy().getCglibProxyInstance(Hanlder.class);
handler.delete("dfdfdfd");
}
}
public class Hanlder {
public void delete(String id) {
System.out.println("删除数据库一条数据, id=" + id );
}
}
CGLib和JDK动态代理对比
1、JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。
2、JDK和CGLib都是在运行期生成字节码,JDK是直接写Class 字节码,CGLib使用ASM框架写Class字节码,Cglib代理实现 更复杂,生成代理类比JDK效率低。
3、JDK调用代理方法,是通过反射机制调用,CGLib是通过 FastClass机制直接调用方法,CGLib执行效率更高。
4、CGLib无法代理final修饰的方法,也无法代理内部类。
Spring 中的代理选择原则
1、当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
2、当 Bean 没有实现接口时,Spring 选择 CGLib。
五、委派模式(Delegate Pattern)
该模式不属于GOF23 种设计模式中。委派模式(Delegate Pattern)的基本作用就是负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果。在springMVC中DispatcherServlet就是很好例子
import java.util.HashMap;
import java.util.Map;
/**
* 委派模式
* 简单列子:老板叫领导干活
*/
public class DelegatePattern {
/**
* 员工接口
*/
public interface Employee {
void doSomething(String commond);
}
/**
* 打印员
*/
public static class EmployeeA implements Employee{
@Override
public void doSomething(String commond) {
System.out.println("打印员接到通知:" + commond);
}
}
/**
* 前台
*/
public static class EmployeeB implements Employee{
@Override
public void doSomething(String commond) {
System.out.println("前台接到命令:" + commond);
}
}
/**
* 领导,不干活,指挥别人干
*/
public static class Leader implements Employee {
private Map<String, Employee> employees = new HashMap<>();
public Leader() {
employees.put("前台", new EmployeeB());
employees.put("打印员", new EmployeeA());
}
@Override
public void doSomething(String commond) {
employees.get(commond).doSomething(commond);
}
}
/**
* 老板下达命令
*/
public static class Boss {
public void commond(String commond) {
new Leader().doSomething(commond);
}
}
public static void main(String[] args) {
//客户请求(Boss)、委派者(Leader)、被被委派者(Target)
//委派者要持有被委派者的引用
//代理模式注重的是过程, 委派模式注重的是结果
//策略模式注重是可扩展(外部扩展),委派模式注重内部的灵活和复用
//委派的核心:就是分发、调度、派遣
//委派模式:就是静态代理和策略模式一种特殊的组合
new Boss().commond("前台");
}
}
六、策略模式(Strategy Pattern)
import java.sql.ParameterMetaData;
import java.util.HashMap;
import java.util.Map;
/**
* 策略模式
* 简单列子:支付方式的选择
*/
public class StrategyPattern {
/**
* 支付抽象
*/
public interface Payment {
void pay(int mount);
}
/**
* 阿里支付
*/
public static class Alipay implements Payment {
@Override
public void pay(int mount) {
System.out.println("阿里支付,支付金额:" + mount);
}
}
/**
* 微信支付
*/
public static class Wechatpay implements Payment {
@Override
public void pay(int mount) {
System.out.println("微信支付,支付金额:" + mount);
}
}
public static class PaymentFactory {
private static Map<String, Payment> payMentMap = new HashMap<>();
private static Payment defaultPayMent = new Alipay();
public PaymentFactory() {
payMentMap.put(PayType.ALI_PAY, new Alipay());
payMentMap.put(PayType.WECHAT_PAY, new Wechatpay());
}
public static Payment getPayMent(String type) {
Payment payment = payMentMap.get(type);
return payment == null ? defaultPayMent : payment;
}
}
public interface PayType {
public static final String ALI_PAY = "ALI_PAY";
public static final String WECHAT_PAY = "WECHAT_PAY";
}
public static void main(String[] args) {
Payment payment = PaymentFactory.getPayMent(PayType.WECHAT_PAY);
payment.pay(234);
}
}
七、模版方法模式
package com.zy.java.design.patterns.template;
/**
* @AUTHOR zhangy
* 2020-10-11 14:22
* 模版方法模式(Template Method Pattern)是指定义一个算法的骨 架,并允许子类为一
* 个或者多个步骤提供实现。
*/
public abstract class TemplateMethodPattern {
/**
* 创建订单
* @param info
*/
protected final void createOrder(Object info) {
// 创建订单
saveOrderInfo(info);
// 支付
pay(info);
// 产生物流信息
saveWuliuInfo(info);
// 发送短信给用户
sendMessage(info);
// .....
}
private final void sendMessage(Object info) {
System.out.println("发送短信");
}
protected final void saveWuliuInfo(Object info) {
System.out.println("产生物流信息");
}
/**
* 交给不同的子类实现 可能有不同的支付方式:支付宝 微信 。。。
* @param info
*/
protected abstract void pay(Object info);
protected final void saveOrderInfo(Object info) {
System.out.println("产生一条订单:insert into order_info ....");
}
}
模板模式的优缺点
优点:
1、利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
2、将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
3、把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台, 符合开闭原则。
缺点:
1、类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加。
2、类数量的增加,间接地增加了系统实现的复杂度。
3、继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。 模板方法模式比较简单,相信小伙伴们肯定能学会,也肯定能理解好!只要勤加练习, 多结合业务场景思考问题,就能够把模板方法模式运用好。
八 适配器模式(adapter pattern)
一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以一起工作,属于结构型设计模式
/**
* 适配器模式(adapter pattern),不改变原有规则的前提下适应新的规则需求
*/
public class AdaptePattern {
public static class LoginService {
public Object login(String userName, String password) {
System.out.println("账号密码登录");
return new Object();
}
}
/**
* 登录业务适配的抽象
*/
public interface LoginAdapter {
Object login(LoginAdapter adapter);
boolean support(Object adapter);
}
public static class LoginForQQ implements LoginAdapter {
public Object login(LoginAdapter adapter) {
System.out.println("qq 登录");
return new Object();
}
public boolean support(Object adapter) {
return adapter instanceof LoginForQQ;
}
}
public static class LoginForWechat implements LoginAdapter {
public Object login(LoginAdapter adapter) {
System.out.println("wechat 登录");
return new Object();
}
public boolean support(Object adapter) {
return adapter instanceof LoginForWechat;
}
}
/**
* 第三方登录
*/
public interface LoginForthirdPart {
Object loginForQQ();
Object loginForWechat();
}
/**
*扩展原有的业务逻辑,不改变原有代码,适应新的需求
*/
public static class LoginForthirdPartService extends LoginService implements LoginForthirdPart {
public Object loginForQQ() {
return doLogin(LoginForQQ.class);
}
public Object loginForWechat() {
return doLogin(LoginForWechat.class);
}
private Object doLogin (Class<? extends LoginAdapter> clazz) {
try {
LoginAdapter adapter = clazz.newInstance();
if (adapter.support(adapter) ) {
return adapter.login(adapter);
} else {
return null;
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
new LoginForthirdPartService().loginForWechat();
}
}
适配器模式的优缺点
优点:
1、能提高类的透明性和复用,现有的类复用但不需要改变。
2、目标类和适配器类解耦,提高程序的扩展性。
3、在很多业务场景中符合开闭原则。
缺点:
1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
九、装饰者模式(Decorator Pattern)
装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对
象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。
/**
* 装饰者模式(Decorator Pattern)
* 列:煎饼加香肠、加鸡蛋
*/
public class DecoratorPattern {
public static abstract class BatterCake {
protected abstract String getBatterCake();
protected abstract int getPrice();
}
/**
* 煎饼类
*/
public static class BaseBatterCake extends BatterCake{
@Override
protected String getBatterCake() {
return "煎饼";
}
@Override
protected int getPrice() {
return 5;
}
}
public static abstract class BatterCakeDecorator extends BatterCake {
// 委派 静态代理
private BatterCake batterCake;
public BatterCakeDecorator(BatterCake batterCake) {
this.batterCake = batterCake;
}
@Override
protected String getBatterCake() {
return this.batterCake.getBatterCake();
}
@Override
protected int getPrice() {
return this.batterCake.getPrice();
}
abstract void doSomething();
}
public static class BatterCakeWithEgg extends BatterCakeDecorator {
public BatterCakeWithEgg(BatterCake batterCake) {
super(batterCake);
}
@Override
protected String getBatterCake() {
return super.getBatterCake() + "鸡蛋";
}
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
@Override
void doSomething() {
System.out.println("business ....");
}
}
public static class BatterCakeWithEggAndHotDogs extends BatterCakeDecorator {
public BatterCakeWithEggAndHotDogs(BatterCake batterCake) {
super(batterCake);
}
@Override
protected String getBatterCake() {
return super.getBatterCake() + "鸡蛋 + 肠";
}
@Override
protected int getPrice() {
return super.getPrice() + 3;
}
@Override
void doSomething() {
System.out.println("business ....");
}
}
public static void main(String[] args) {
BatterCake cake = new BaseBatterCake();
System.out.println(cake.getBatterCake());
cake = new BatterCakeWithEgg(cake);
System.out.println(cake.getBatterCake());
cake = new BatterCakeWithEggAndHotDogs(cake);
System.out.println(cake.getBatterCake());
}
}
十、观察者模式(Observe Pattern)
观察者模式定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,属于行为型模式。
/**
* 观察者模式(Observe Pattern)
* 场景:学生反馈问题,老师接收
*/
public class ObservePattern {
public static class Question {
private String user;
private String questionDescribe;
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getQuestionDescribe() {
return questionDescribe;
}
public void setQuestionDescribe(String questionDescribe) {
this.questionDescribe = questionDescribe;
}
}
/**
* JDK提供观察者、被观察者模式
*/
public static class MesageObserve extends Observable {
private static MesageObserve mesageObserve = null;
private String name = "学生管理平台";
public String getName() {
return name;
}
public static MesageObserve getInstance() {
if (null == mesageObserve) {
mesageObserve = new MesageObserve();
}
return mesageObserve;
}
public void publishQuestion(Question question) {
System.out.println("学生:" + question.getUser() + "在平台上反馈了一个问题");
setChanged();
notifyObservers(question);
}
}
public static class Teacher implements Observer{
private String name;
public Teacher(String name){
this.name = name;
}
public void update(Observable o, Object arg) {
MesageObserve gper = (MesageObserve)o;
Question question = (Question)arg;
System.out.println("===============================");
System.out.println(name + "老师,你好!\n" +
"您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" +
question.getQuestionDescribe() + "\n" +
"提问者:" + question.getUser());
}
}
public static void main(String[] args) {
MesageObserve mesageObserve = MesageObserve.getInstance();
Teacher zhang = new Teacher("zhang");
Teacher huang = new Teacher("huang");
mesageObserve.addObserver(zhang);
mesageObserve.addObserver(huang);
Question question = new Question();
question.setUser("小明");
question.setQuestionDescribe("装饰者模式怎么使用。。。");
mesageObserve.publishQuestion(question);
}
}
十一、责任链模式(Chain of Responsibility)
责任链模式么一个节点看做是一个对象,每一个节点处理不同业务逻辑,且内部维护下一个节点对象。
/**
* 责任链模式(Chain of Responsibility)
* 列:对参数校验(非空、登录、权限)
*/
public class ChainOfResponsibility {
public static class Params {
private String userName;
private String token;
private String userRule;
public String getUserName() {
return userName;
}
########省略部分get set###########
}
public static abstract class Handler {
public Handler chain;
public void setNextHandler (Handler handler) {
this.chain = handler;
}
public abstract void doHandler (Params params);
}
/**
* 非空参数验证Handler
*/
public static class VolidateHandler extends Handler {
@Override
public void doHandler(Params params) {
System.out.println("参数非空验证通过。。。");
chain.doHandler(params);
}
}
/**
* 角色验证Handler
*/
public static class RoleHandler extends Handler {
@Override
public void doHandler(Params params) {
System.out.println("角色验证通过。。。");
chain.doHandler(params);
}
}
/**
* 权限验证Handler
*/
public static class AuthorHandler extends Handler {
@Override
public void doHandler(Params params) {
System.out.println("权限验证通过。。。");
if (null != chain) {
chain.doHandler(params);
}
}
}
public static class HandlerService {
public boolean userLogin(String userName) {
Params params = new Params();
params.setUserName(userName);
params.setToken("token");
params.setUserRule("admin");
VolidateHandler volidateHandler = new VolidateHandler();
RoleHandler roleHandler = new RoleHandler();
AuthorHandler authorHandler = new AuthorHandler();
volidateHandler.setNextHandler(roleHandler);
roleHandler.setNextHandler(authorHandler);
volidateHandler.doHandler(params);
System.out.println("login success");
return true;
}
}
public static void main(String[] args) {
new HandlerService().userLogin("zhangsan ");
}
}
文章详情查看: http://www.xiaoyuge.com.cn/#/article/detail?articleId=67