# 设计模式java **Repository Path**: yang-kairui/design-pattern ## Basic Information - **Project Name**: 设计模式java - **Description**: 设计模式java案例 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2022-03-31 - **Last Updated**: 2023-03-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式JAVA案例 > 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 > 常见的设计模式分为三种类型,共23种: ## 行为型模式: ### 1、观察者模式(买彩票) > 定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。 JAVA的JDK和很多框架都用到了观察者模式,最典型的比如说spring mvc中的DispatcherServelet以及Zookeeper的动态监控注册中心,感兴趣的话可以去阅读一下源码 ![](img/观察者模式图解.jpg) 如上图所示,服务号就是我们的主题,使用者就是观察者。现在我们明确下功能: 1、服务号就是主题,业务就是推送消息 2、观察者只需要订阅主题,只要有新的消息就会送来 3、当不想要此主题消息时,取消订阅 4、只要服务号还在,就会一直有人订阅 好了,现在我们来看看观察者模式的类图: ![](img/观察者模式类图.jpg) 接下来就是代码时间了,我们模拟一个买彩票的场景,彩票站就是主题,买彩票的人就是观察者。 首先定义两个接口,主题接口[Subject](code/behavior/observer/Subject.java)、订阅者接口[Observer](code/behavior/observer/Observer.java) ```java /** * 所有的被观察主题需要实现该接口 * * @author ykr * @date 2022/3/31 */ public interface Subject { /** * 存储所有观察者 * @return List */ List getObservers(); /** * 注册一个观察者 * @param observer 订阅的观察者对象 */ void registerObserver(Observer observer); /** * 移除一个观察者 * @param observer 订阅的观察者对象 */ void removeObserver(Observer observer); /** * 通知所有观察者 */ void notifyObservers(); } ``` ```java /** * 所有订阅者需要实现该接口 * * @author ykr * @date 2022/3/31 */ public interface Observer { void update(String msg); } ``` 接下来实现一个彩票主题[LotterySubject ](code/behavior/observer/test/LotterySubject.java)和彩民订阅者[LotteryHolders](code/behavior/observer/test/LotteryHolders.java) ```java /** * 彩票主题 * * @author ykr * @date 2022/3/31 */ public class LotterySubject implements Subject { /** * 所有买彩票的彩民 */ private List observers = new ArrayList<>(); /** * 开奖号码 */ private String msg; @Override public List getObservers() { return this.observers; } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(observer); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(msg); } } /** * 开奖 */ public void lottery(String msg) { this.msg = msg; notifyObservers(); } } ``` ```java /** * 持有彩票的人 * * @author ykr * @date 2022/3/31 */ public class LotteryHolders implements Observer { private String lottery; LotteryHolders(String lottery) { this.lottery = lottery; } @Override public void update(String msg) { //开奖 System.out.println("本期开奖号码:" + msg + ",我的号码:" + lottery); } } ``` 最后模拟买彩票案例[LotteryTest ](code/behavior/observer/test/LotteryTest.java) ```java /** * 买彩票模拟 * * @author ykr * @date 2022/3/31 */ public class LotteryTest { public static void main(String[] args) { LotteryHolders li = new LotteryHolders(String.valueOf(new Random().nextInt(10))); LotteryHolders wang = new LotteryHolders(String.valueOf(new Random().nextInt(10))); LotteryHolders yang = new LotteryHolders(String.valueOf(new Random().nextInt(10))); LotterySubject lotteryStore = new LotterySubject(); lotteryStore.registerObserver(li); lotteryStore.registerObserver(wang); lotteryStore.registerObserver(yang); lotteryStore.lottery("5"); } } ``` 执行结果 `本期开奖号码:5,我的号码:4 本期开奖号码:5,我的号码:9 本期开奖号码:5,我的号码:7` ### 2、状态模式(打boss) > 定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。 实际上就是模拟一个有限状态机,不同状态采取不同的处理方式,我们以一个打boss的小案例为例: 先创建一个Boss实体类 ```java public class Boss { /** * 名称 */ private String name; /** * 生命值 */ private double life; /** * 防御力 */ private double def; //Getter Setter... } ``` 创建一个状态机顶级接口: ```java public interface State { /** * 玩家攻击 * * @param atk 玩家攻击力 * @param crit 玩家暴击倍率 * @return */ void attack(double atk, double crit); } ``` 创建三个状态机,分别是防御、暴击、击倒 ```java public class DefendState implements State { private Boss boss; public DefendState(Boss boss) { this.boss = boss; } @Override public void attack(double atk, double crit) { double dmg = atk - boss.getDef(); boss.setLife(boss.getLife() - dmg); System.out.println("对"+boss.getName()+"发起了攻击,造成了" + dmg + "点伤害," + (boss.getLife() > 0 ? "boss剩余血量" + boss.getLife() : "boss已被击倒!")); } } ``` ```java public class CriticalState implements State { private Boss boss; public CriticalState(Boss boss) { this.boss = boss; } @Override public void attack(double atk, double crit) { double dmg = atk * crit - boss.getDef(); boss.setLife(boss.getLife() - dmg); System.out.println("对" + boss.getName() + "造成了暴击,造成了" + dmg + "点伤害," + (boss.getLife() > 0 ? "boss剩余血量" + boss.getLife() : "boss已被击倒!")); } } ``` ```java public class DeadState implements State { private Boss boss; public DeadState(Boss boss) { this.boss = boss; } @Override public void attack(double atk, double crit) { System.out.println("boss" + boss.getName() + "已被击倒,无法造成伤害"); } } ``` 最后,我们来测试打boss过程,看看状态是怎么影响打boss行为的 ```java public class StateTest { public static void main(String[] args) { Boss boss = new Boss("哥布林国王", 100.0, 20.0); System.out.println(boss.getName() + "出现了,血量" + boss.getLife() + ",防御力" + boss.getDef()); new DefendState(boss).attack(60.0, 0.0); System.out.println("获得装备无尽之刃,攻击变为暴击,暴击倍率225%"); new CriticalState(boss).attack(60.0, 2.25); new DeadState(boss).attack(40.0, 2.25); } } ``` `哥布林国王出现了,血量100.0,防御力20.0` `对哥布林国王发起了攻击,造成了40.0点伤害,boss剩余血量60.0` `获得装备无尽之刃,攻击变为暴击,暴击倍率225%` `对哥布林国王造成了暴击,造成了115.0点伤害,boss已被击倒!` `boss哥布林国王已被击倒,无法造成伤害` ### 3、策略模式(释放技能) > 策略模式:定义了算法族,分别封装起来,让它们之间可相互替换,此模式让算法的变化独立于使用算法的客户。 我们需要定义一个抽象策略,然后实现一系列的具体策略,在我们执行的时候可以运行特定策略的类。 以释放技能为例: 先创建使用算法的客户,角色 ```java public class Role { /** * 名字 */ private String name; /** * 技能 */ private Skill skill; public void skill() { System.out.println(name + "释放技能"); skill.skill(); } //Getter and Setter... } ``` 然后创建抽象策略,技能 ```java public interface Skill { /** * 释放技能 */ void skill(); } ``` 实现两个具体策略 ```java public class Skill1 implements Skill{ @Override public void skill() { System.out.println("释放降龙十八掌"); } } ``` ```java public class Skill2 implements Skill{ @Override public void skill() { System.out.println("释放九阳神功"); } } ``` 测试一下 ```java public class StrategeTest { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Role role = new Role("张三"); while (true) { if (1 == scanner.nextInt()){ role.setSkill(new Skill1()); role.skill(); } if (2 == scanner.nextInt()){ role.setSkill(new Skill2()); role.skill(); } } } } ``` 执行结果: `1 张三释放技能 释放降龙十八掌 2 张三释放技能 释放九阳神功` 策略模式其实和状态模式很像,但是实现和用法不一样,策略模式的意图是为了拓展性和解耦。 ## 结构型模式: ### 1、装饰模式 ## 创建型模式: ### 1、工厂模式(模拟IoC容器) > 通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。 通过简单模拟spring的IoC容器来尝试工厂模式的使用: 先请出主角ObjectFactory,他只有一个方法getObject(),用来生产对象 ```java @FunctionalInterface public interface ObjectFactory { /** * 返回由该工厂管理的对象的一个实例(可能是共享的或独立的)。 * @return 对象的一个实例 */ T getObject() throws Exception; } ``` 再写一个接口BeanFactory用来封装管理bean对象的方法,我只模拟两个方法,一个用来获取bean,一个用来获取bean的工厂 ```java public interface BeanFactory { /** * 通过名称获取bean * @param name bean的名称 * @param requiredType bean的字节对象 * @param bean的类型 * @return */ T getBean(String name,Class requiredType) throws Exception; /** * 获取生成bean的工厂 * * @param bean的类型 * @return bean生产工厂 */ ObjectFactory getObjectFactory(Class requiredType); } ``` 最后实现spring的ioc容器 ```java public class ApplicationContext implements BeanFactory { private final HashMap context = new HashMap<>(); @Override @SuppressWarnings("unchecked") public T getBean(String name, Class requiredType) throws Exception { synchronized (this.context) { Object bean = this.context.get(name); if (null != bean) { return (T) bean; } bean = getObjectFactory(requiredType).getObject(); this.context.put(name, bean); return (T) bean; } } @Override public ObjectFactory getObjectFactory(Class requiredType) { return requiredType::newInstance; } } ``` 创建一个map,作为容器保存我们的bean对象,当我们通过name去获取bean,如果容器中存在则直接返回,如果容器中不存在,则通过我们的工厂去生产bean,然后存储在容器中,值得一提的事,工厂中生产bean的方法可以随意实现,这里采用了反射去生产,这也是工厂模式的作用。