前言:鸿洋大神的设计模式系列博客已经阅读完毕,下面来做个总结。
Java 设计模式(观察者模式、工厂模式、单例模式、策略模式、命令模式、装饰者模式、外观模式、模板方法模式、状态模式)
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
设计模式分为三种类型,共23种:
- 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。
Pattern Analysis
1. 观察者模式
定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
- 对于JDK或者Andorid中都有很多地方实现了观察者模式,比如XXXView.addXXXListenter , 当然了 XXXView.setOnXXXListener不一定是观察者模式,因为观察者模式是一种一对多的关系,对于setXXXListener是1对1的关系,应该叫回调。
- 专题接口:Subject.java ;
· /**
· *注册一个观察者
· */
· publicvoidregisterObserver(Observer observer);
·
· /**
· *移除一个观察者
· */
· publicvoidremoveObserver(Observer observer);
·
· /**
· *通知所有观察者
· */
publicvoidnotifyObservers();
- 3D服务号的实现类:ObjectFor3D.java
· @Override
· publicvoidregisterObserver(Observer observer) {
· observers.add(observer);
· }
· @Override
· publicvoidremoveObserver(Observer observer) {
· int index= observers.indexOf(observer);
· if (index>=0) {
· observers.remove(index);
· }
· }
· @Override
· publicvoidnotifyObservers() {
· for (Observerobserver: observers) {
· observer.update(msg);
· }
· }
· /**
· *主题更新信息
· */
· publicvoid setMsg(Stringmsg) {
· this.msg= msg;
· notifyObservers();
}
- 所有观察者需要实现此接口:Observer.java
· publicObserverUser1(Subject subject) {
· subject.registerObserver(this);
· }
· @Override
· publicvoid update(Stringmsg) {
· Log.e("-----ObserverUser1","得到 3D号码:"+ msg+",我要记下来。 ");
}
· //创建服务号
· objectFor3D=new ObjectFor3D();
· //创建两个订阅者
· observerUser1=newObserverUser1(objectFor3D);
· observerUser2=newObserverUser2(objectFor3D);
· //两个观察者,发送两条信息
· objectFor3D.setMsg("201610121的3D号为:127");
objectFor3D.setMsg("20161022的3D号为:000");
2. 工厂模式
简单列一下这个模式的家族:
- 1、静态工厂模式
- 这个最常见了,项目中的辅助类,TextUtil.isEmpty等,类+静态方法。
- 2、简单工厂模式(店里买肉夹馍)
- 定义:通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
- 根据类型直接创建肉夹馍:SimpleRoujiaMoFactory.java
· public RoujiaMocreatRoujiaMo(String type) {
· RoujiaMo roujiaMo=null;
· switch (type) {
· case"Suan":
· roujiaMo=newZSuanRoujiaMo();
· break;
· case"La":
· roujiaMo=new ZLaRoujiaMo();
· break;
· case"Tian":
· roujiaMo=new ZTianRoujiaMo();
· break;
· default://默认为酸肉夹馍
· roujiaMo=newZSuanRoujiaMo();
· break;
· }
· return roujiaMo;
}
- 3、工厂方法模式(开分店)
- 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类。
- 对比定义:
- 1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
- 2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
- 提供创建肉夹馍店抽象方法:RoujiaMoStore.java
publicabstract RoujiaMo sellRoujiaMo(String type);
- 具体实现抽象方法:XianRoujiaMoStore.java
- 分店依旧使用简单工厂模式:XianSimpleRoujiaMoFactory.java
- 4、抽象工厂模式(使用官方提供的原料)
- 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
- 对比定义:
- 1、提供一个接口:public interface RouJiaMoYLFactroy
- 2、用于创建相关的或依赖对象的家族 public Meat createMeat();public YuanLiao createYuanliao();我们接口用于创建一系列的原材料。
- 创建用于提供原料的接口工厂:RoujiaMoYLFactory.java
- 各自分店实现接口,完成原料提供:XianRoujiaMoYLFoctory.java
- 准备时,使用官方的原料:RoujiaMo.java
o /**
o *准备工作
o */
o publicvoidprepare(RoujiaMoYLFactory roujiaMoYLFactory) {
o Meet meet=roujiaMoYLFactory.creatMeet();
o YuanLiao yuanLiao=roujiaMoYLFactory.creatYuanLiao();
o Log.e("---RoujiaMo:","使用官方的原料 ---"+ name+":揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
}
3. 单例设计模式
单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。
- 定义:只需要三步就可以保证对象的唯一性
- (1) 不允许其他程序用new对象
- (2) 在该类中创建对象
- (3) 对外提供一个可以让其他程序获取该对象的方法
- 对比定义:
- (1) 私有化该类的构造函数
- (2) 通过new在本类中创建一个本类对象
- (3) 定义一个公有的方法,将在该类中所创建的对象返回
- 饿汉式[可用]:SingletonEHan.java
- 含懒汉式[双重校验锁 推荐用]:SingletonLanHan.java
· private SingletonLanHan(){}
· privatestatic SingletonLanHansingletonLanHanFour;
· publicstatic SingletonLanHangetSingletonLanHanFour() {
· if(singletonLanHanFour==null) {
· synchronized(SingletonLanHan.class) {
· if(singletonLanHanFour==null) {
· singletonLanHanFour=newSingletonLanHan();
· }
· }
· }
· returnsingletonLanHanFour;
· }
- 内部类[推荐用]:SingletonIn.java
- 枚举[推荐用]:SingletonEnum.java
4. 策略模式
策略模式:定义了算法族,分别封装起来,让它们之间可相互替换,此模式让算法的变化独立于使用算法的客户。
- 以创建游戏角色为例子:
- 总结:
- 1、封装变化(把可能变化的代码封装起来)
- 2、多用组合,少用继承(我们使用组合的方式,为客户设置了算法)
- 3、针对接口编程,不针对实现(对于Role类的设计完全的针对角色,和技能的实现没有关系)
- 最后测试:创建角色:
· RoleA roleA=new RoleA("---A");
· roleA.setiDisplayBehavior(new DisplayYZ())
· .setiAttackBehavior(new AttackXL())
· .setiDefendBehavior(new DefendTMS())
· .setiRunBehavior(new RunJCTQ());
· roleA.display();//样子
· roleA.attack();//攻击
· roleA.run();//逃跑
roleA.defend();//防御
5. 适配器模式
定义:将一个类的接口转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以相互合作。这个定义还好,说适配器的功能就是把一个接口转成另一个接口。
- 以充电器为实例: 手机充电器一般都是5V左右吧,咱天朝的家用交流电压220V,所以手机充电需要一个适配器(降压器)
- 一部手机: Mobile.java
- 手机依赖一个提供5V电压的接口: V5Power.java
- 我们拥有的是220V家用交流电: V220Power.java
- 适配器,完成220V转5V的作用:V5PowerAdapter.java
- 最后测试:给手机冲个电:
· Mobile mobile=new Mobile();
· V5Power v5Power=new V5PowerAdapter(new V200Power());
mobile.inputPower(v5Power);
6. 命令模式
定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。(简化:将请求封装成对象,将动作请求者和动作执行者解耦。)
- 需求:最近智能家电很火热,假设现在有电视、电脑、电灯等家电,现在需要你做个遥控器控制所有家电的开关,要求做到每个按钮对应的功能供用户个性化,对于新买入家电要有非常强的扩展性。
- 1、家电的API:Door.java
- 2、把命令封装成类:
- 统一的命令接口:Command.java
- 家电实现该接口:DoorOpenCommand.java
- 3、遥控器:ControlPanel.java
- 4、定义一个命令,可以干一系列的事情:QuickCommand.java
· QuickCommand quickCloseCommand=new QuickCommand(new Command[]{newLightOffCommand(light),newComputerOffCommand(computer),new DoorCloseCommand(door)});
· controlPanel.setCommands(6,quickOpenCommand);
controlPanel.keyPressed(6);
- 5、遥控器面板执行:CommandActivity.java
· controlPanel.setCommands(0,newDoorOpenCommand(door));//开门
controlPanel.keyPressed(0);
7. 装饰者模式
装饰者模式:若要扩展功能,装饰者提供了比集成更有弹性的替代方案,动态地将责任附加到对象上。
- 先简单描述下装饰者模式发挥作用的地方,当我们设计好了一个类,我们需要给这个类添加一些辅助的功能,并且不希望改变这个类的代码,这时候就是装饰者模式大展雄威的时候了。这里还体现了一个原则:类应该对扩展开放,对修改关闭。
- 需求:设计游戏的装备系统,基本要求,要可以计算出每种装备在镶嵌了各种宝石后的攻击力和描述:
- 1、装备的超类:IEquip.java
- 2、各个装备的实现类:
- eg:武器的实现类: ArmEquip.java
- 3、装饰品的超类(装饰品也属于装备):IEquipDecorator.java
- 4、装饰品的实现类:
- eg:蓝宝石的实现类(可累加): BlueGemDecorator.java
- 5、最后测试:计算攻击力和查看描述:
· Log.e("---","一个镶嵌2颗红宝石,1颗蓝宝石的靴子: ");
· IEquip iEquip=newRedGemDecotator(new RedGemDecotator(newBlueGemDecotator(new ShoeEquip())));
· Log.e("---","攻击力:"+ iEquip.caculateAttack());
Log.e("---","描述语:"+ iEquip.description());
8. 外观模式
定义:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。其实就是为了方便客户的使用,把一群操作,封装成一个方法。
- 需求:我比较喜欢看电影,于是买了投影仪、电脑、音响、设计了房间的灯光、买了爆米花机,然后我想看电影的时候,我需要一键观影和一键关闭。
- 每个设备类的开关等操作:
- eg: 爆米花机:PopcornPopper.java
- 电影院类:HomeTheaterFacade.java
· /**
· *一键观影
· */
· publicvoid watchMovie() {
· computer.on();
· light.down();
· popcornPopper.on();
· popcornPopper.makePopcorn();
· projector.on();
· projector.open();
· player.on();
· player.make3DListener();
}
- 最后测试:一键观影:
new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
9. 模板方法模式
定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。
- 需求:简单描述一下:本公司有程序猿、测试、HR、项目经理等人,下面使用模版方法模式,记录下所有人员的上班情况
- 模板方法模式中的三类角色
- 1、具体方法(Concrete Method)
- 2、抽象方法(Abstract Method)
- 3、钩子方法(Hook Method)
- 工人的超类:Worker.java
· //具体方法
· publicfinalvoid workOneDay() {
· Log.e("workOneDay","-----------------workstart----------------");
· enterCompany();
· work();
· exitCompany();
· Log.e("workOneDay","-----------------workend----------------");
· }
· //工作 抽象方法
· publicabstractvoid work();
· //钩子方法
· publicbooleanisNeedPrintDate() {
· returnfalse;
· }
· privatevoid exitCompany() {
· if(isNeedPrintDate()) {
· Log.e("exitCompany","---"+new Date().toLocaleString()+"--->");
· }
· Log.e("exitCompany", name +"---离开公司");
}
- 程序员实现类(可得知时间):ITWorker.java
· /**
· *重写父类的此方法,使可以查看离开公司时间
· */
· @Override
· publicbooleanisNeedPrintDate() {
· returntrue;
}
- 最后测试:
- 查看所有人员的工作情况:
o QAWorkerqaWorker=new QAWorker("测试人员");
o qaWorker();
o HRWorkerhrWorker=new HRWorker("莉莉姐");
o hrWorker.workOneDay();
...
- 查看程序猿离开公司的时间:
o ITWorkeritWorker=new ITWorker("jingbin");
itWorker.workOneDay();
10. 状态模式
定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
- 定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的。
- 需求:已自动售货机为例(有已投币、未投币等状态和投币、投币等方法)
- 最初实现待改进的售货机:VendingMachine.java
- 改进后的售货机(更具有延展性):VendingMachineBetter.java
· //放钱
· publicvoid insertMoney() {
· currentState.insertMoney();
· }
· //退钱
· publicvoid backMoney() {
· currentState.backMoney();
· }
· //转动曲柄
· publicvoid turnCrank() {
· currentState.turnCrank();
· if (currentState== soldState|| currentState== winnerState) {
· currentState.dispense();//两种情况会出货
· }
· }
· //出商品
· publicvoid dispense() {
· Log.e("VendingMachineBetter","---发出一件商品");
· if (count>0) {
· count--;
· }
· }
· //设置对应状态
· publicvoid setState(Statestate) {
· this.currentState= state;
}
- 状态的接口:State.java
- 对应状态的接口实现类:
- eg: 中奖状态:WinnerState.java
- eg: 售卖状态:SoldState.java
- 改进后的售货机测试:
· //初始化售货机,且里面有3个商品
· VendingMachineBetter machineBetter=newVendingMachineBetter(3);
· machineBetter.insertMoney();
machineBetter.turnCrank();
Download
Thanks
源码地址链接:https://github.com/371894545/Pattern#pattern