大话设计模式(更新ing...)
目录
单例模式:
简单工厂模式
工厂方法模式
抽象工厂模式
策略模式
观察者模式
适配器模式
模板方法模式(模板模式)
装饰者模式
静态代理模式
动态代理模式
责任链模式
享元模式
迭代器模式
单例模式:
方式一:拿去吧!
如果一个对象被new了多次,但无法保证每个对象都会被使用,这时候就会很浪费空间,而且如果访问量巨大,服务器也要承担很大的压力。出现这种情况,一些Java大神们当然不会放任着不管,于是经过一番潜心研究,就出现了一种新的获得对象的方式——单例模式。
单例模式大概做的事情就是通过自己的设计,让当前的这个类只能产生一个对象(但是根据特定情况以及自己对于这一块的设计,不能一刀切的都去用这个方法,比如一个person类,每个person都有自己不同的属性,怎么能都用一个person来做事情呢)。
如果做?
- 保证当前类的构造方法不能被随便调用吧,这是最基本的条件。也就是让构造方法私有化--private XXX(){}
- 不能调用构造方法,那我怎么获取这个对象呢。有四种方式:
- 构造方法
- 代码块
- 普通方法
- 属性
构造方法:前面我们为了不能随便创建当前对象,已经给构造方法私有化,所以排除此方法。
代码块:代码块会在对象创建的时候就加载了,但是没有返回值,同样获取不到对象,排除。
普通方法:这个似乎和构造方法一样了,每次调用这个方法同样会创建一个对象出来,也无法做到对象时单例的。
属性: 似乎只能使用属性来new对象,然后通过方法来将对象传递。
public Singleton single = new Singleton();
上面这个写法很明显是不对的,而且在做单例上面,似乎没有任何存在的意义。
原因是啥呢?
首先,这样写上来就是一个StackOverflow Error。因为这是类里面的属性,类加载以后会加载自己里面的属性,方法等。但是当加载这个属性的时候,发现又new了一个自己,然后又去加载自己类,又遇到一个属性里面new了一个自己。。。陷入死循环导致栈溢出。
解决方法:保证是一份就OK啦。 public static Singleton single = new Singleton();
有些朋友可能看出还有一个问题:就是这样写我岂不是用类名就可以随便调用了,Singleton.single简直不要太简单。所以呢,为了让我的获取对象的方法更有存在感,必然是不能让您调用的嗷。
较为标准的写法:private static Singleton single = new Singleton();
好啦。属性对象创建好了,我怎么给你呢--->通过方法获得。 设计一个方法,将当前唯一的对象返回出去。
public static Singleton getInstance(){//注意:这里return的是single属性中存储对象的地址引用return single;}
这种方式又叫做饿汉式,也就是:上来就创建一个对象,如同饿汉见到食物上来就吃。但是呢,这样在整个系统执行过程中,如果创建的对象不使用,就会一直占用着内存,造成了内存空间的浪费。
方式二:不用,我就不做!
这种方式呢,又叫做懒汉式,就是你可以要,但是我未必会创建,等到你用的时候我再创建,这样就很大程度上避免了内存空间浪费问题。写法和饿汉式几乎一致,代码如下:
private Singleton(){}
//我先不创建对象
private static Singleton single;
public static Singleton getInstance(){//这个时候你要用了,但是我要看看是否真的没有创建好的对象吗,没有我再给你if(single == null){single = new Singleton();}return single;
}
有利肯定有弊:这时候要考虑到线程的问题了,比如A和B两个线程,同时需要这个对象,假如A先开辟了一块空间single,A正准备判断对象是否为空,这个时候时间片轮转,到B了,B也开辟了一块空间,然后A又创建了对象,但此时B判断对象已经不为空了,但是自己又开辟了一块空间,就比较冲突。当线程数更多的话,资源的争夺就会更明显。所以,请看下个分解。
方式三:我给锁住,让你抢我的东西!
在懒汉式的基础上加一个锁,就是我在用这个对象的时候,我要给他锁住,你们谁也不能动,直到我用完。代码实现如下:
private Singelton(){}
private static Singleton single;
//当前线程使用的时候不允许其他线程操作
public synchronized static Singleton getInstance(){if(single == null){single = new Singleton();}return single;
}
有利有弊:这种整个方法的执行过程中,不能有其他线程操作的方式,线程安全是解决了,万一你用很久,谁等的起啊。
方式四:双重判定!
就是将当前的类模板锁住,而不是锁住整个对象,这样其他线程进来如果对象不是空的,就可以直接返回了,而不需要去等待加锁的线程执行完毕,这样大大提高了执行效率。实现如下:
private Singelton(){}
private static Singleton single;
public static Singleton getInstance(){//第一重判定,如果为空,加锁,创建对象if(single == null){synchronized(Singleton.class){//第二重判定,如果为空 创建对象if(single == null){single = new Singleton();}}}return single;
}
有利有弊:这样在解决了加锁性能的问题的同时,在JVM虚拟机中,在对象的开辟和赋值的过程中,可能会产生指令重排序。本来时1 ——> 2 ——> 3的顺序,可能会变成 1 ——> 3 ——> 2。
- 为
instance
分配内存空间。 - 初始化
instance
。 - 将
instance
指向分配的内存地址。
方式五:大哥来了!
属性上添加volatile,保证属性在加载和赋值的过程中,不会被JVM指令重排序。代码如下:
private Singleton single(){}
private static volatile Singleton single;
public static Singleton getInstance(){if(single == null){synchronized(Singleton.class){if(single == null){single = new Singleton();}}}
}
简单工厂模式
大家所熟知的有工厂方法模式、抽象工厂模式,那么简单工厂模式又是什么呢?简单工厂模式不能算作一个标准的设计模式,但是呢,在开发之中又挺常用的,那么就当做一个热身吧!
举一个小案例:话说有一天晚上,博主突发饿疾,非要去附近的小作坊买一些关东煮吃吃。且附近几家都有卖。那么问题来了,我新来的这个地方,还不熟悉,我还没吃过这附近的关东煮呢,万一关东煮里面有其他东西,出了问题,我找谁去呢??找我买东西的那家店?万一人家无证经营跑路了呢。所以为了安全起见,我需要看到人家的营业执照才放心的买。那么这就要求这些小作坊需要被统一管理起来(颁发营业执照,你这家店可以卖关东煮),或者说的贴切代码一些(遵循一个规则)。
好,假设有三家小作坊店,为了方便理解,类名就用拼音了(其实是我不会英语)。那么:GuanDongZhu1.java GuanDongZhu2.java GuanDongZhu3.java。这是我附近的三家店。现在我要给他们指定一个规则,允许他们卖关东煮。AcceptGuanDongZhu.java。既然是指定规则的类,我们就可以将它指定为借口或者父类。此例中将其指定为借口。那么三个小作坊店都实现这个借口里面的方法(卖关东煮)。代码如下:
GuanDongZhu1.java
public class GuanDongZhu1 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu1号店也售卖关东煮,博主快来吃啊");}
}
GuanDongZhu2.java
public class GuanDongZhu2 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu2号店售卖关东煮,博主快来吃啊");}
}
GuanDongZhu3.java
public class GuanDongZhu3 implements AcceptGuanDongZhu{@Overridepublic void sale() {System.out.println("GuanDongZhu3号店关东煮最好吃,博主快来吃啊");}
}
AcceptGuanDongZhu.java
public interface AcceptGuanDongZhu {//售卖关东煮的接口方法(营业执照) 默认用 public abstract修饰void sale();
}
TestMain.java
public static void main(String[] args) {//多态来创建子类对象AcceptGuanDongZhu sale = new GuanDongZhu1();sale.sale();
}
好,营业执照给了,小作坊也遵循了,但是!新的问题来了。用户这样用,是不是知道的太多了。首先,他知道了我的接口规则(实现了AcceptGuanDongZhu这个接口),其次,我具体的实现类也让他知道了,这完全违背封装隔离的思想啊。那么我就需要把用户和店隔开,用户可以看见我的实现规则,但是呢,我不能让你知道我的具体实现类是哪个,你可以通过传参数的方式,告知我想去哪个店,我给你找。而中间这个隔离层呢,就是我们今天的重点了——Factory。就是你告诉我工厂,你要买关东煮了,如果你传给我一个参数(店名),那我就把这个店给到你,不传,我就随便给你一个。按照这种封装隔离的思想,把具体的实现类包装起来。代码实现如下:
GuanDongZhuFactory.java
//调用我工厂 我要给你返回一个店 你给我传一个店名 或者不传我就随机给你一个public AcceptGuanDongZhu getGuanDongZhu(String name){//下面就简单做个判断,重要的是思想if(!"".equals(name) && "GuanDongZhu1".equals(name)){return new GuanDongZhu1();}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){return new GuanDongZhu2();}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){return new GuanDongZhu3();}else{//啥也不传,我给你选一个return new GuanDongZhu1();}}
那么这时候用户找店的方式就应该变成:
TestMain.java
public static void main(String[] args) {//先创建工厂GuanDongZhuFactory factory = new GuanDongZhuFactory();AcceptGuanDongZhu sale = factory.getGuanDongZhu("GuanDongZhu1");sale.sale();}
工厂方法模式
引子:为什么会有工厂方法模式?
在上面的简单工厂模式中,用户通过创建工厂对象来获取目标对象(GuanDongZhu对象),那么由于关东煮不止一家,用户肯定有自己想去的一家,在简单工厂模式中依靠传参数来决定选择哪一家,那这就意味着用户需要了解参数的意义。而且啊,添加了参数,我工厂处理的时候岂不是多了很多的判断,如:下面就是简单工厂中的工厂要做的事情。
if(!"".equals(name) && "GuanDongZhu1".equals(name)){return new GuanDongZhu1();}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){return new GuanDongZhu2();}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){return new GuanDongZhu3();}else{//啥也不传,我给你选一个return new GuanDongZhu1();}
这样,每新开一家店,大工厂就要多一个判断,是不是挺麻烦啊。可是不判断呢,我又不知道该创建哪个具体的对象,唉,好烦,不干了!对,不干了——无为而治。那怎么办?这样吧,你大工厂把自己的方法变成抽象的,然后我再找人去专门创建对象,但是呢,你大工厂要把我创建好的对象给人家用户好吧。“好!”。OK,大工厂的获取对象的方法变成抽象了,那类也就是抽象的了。创建对象的事,我再找人,小弟多。我给每一个关东煮店都配一个创建对象的小弟(小工厂),然后由大工厂去把这个小工厂找过来递给用户。部分代码实现如下:
GuanDongZhuFactory.java 总工厂类
public abstract class GuanDongZhuFactory {//判断太麻烦 不干了! 你再找小弟去创建对象吧,我只负责给你找小弟,然后送给用户public abstract AcceptGuanDongZhu chooseGuanDongZhu();//为了不让用户知道 我大工厂又去找小工厂干活了 掩盖住我懒惰的事实public AcceptGuanDongZhu getGuanDongZhu(){return chooseGuanDongZhu();}
}
GuanDongZhu1Factory.java 每个店的小工厂(负责创建对象)
public class GuanDongZhu1Factory extends GuanDongZhuFactory{//一号店的小弟 创建了一号店对象@Overridepublic AcceptGuanDongZhu chooseGuanDongZhu() {return new GuanDongZhu1();}
}
TestMain.java
public static void main(String[] args) {//大工厂拿着小工厂给创建好的对象 借花献佛 送给了用户 卖关东煮成功GuanDongZhuFactory factory = new GuanDongZhu1Factory();AcceptGuanDongZhu sale = factory.getGuanDongZhu();sale.sale();}
给每一家店都分配一个小工厂,这个小工厂只创建这一家的对象,就省去了判断的步骤,用户直接创建对应的那一家店,这样只暴露给用户工厂类,具体实现类用户是不知道的。
所谓工厂方法,中的方法指的就是总工厂里面的抽象方法,小工厂通过继承大工厂,然后重写这个方法实现对象创建。 工厂方法模式中的 “ 方法 ”。
public abstract AcceptGuanDongZhu chooseGuanDongZhu();
抽象工厂模式
模拟计算机组件(主板,CPU)来描述此模式,(相关知识可能有误,见谅)先实现没有抽象工厂模式,只是简单工厂模式,将两者进行对比。
简单工厂模式实现如下:
AMDCPU.java
public class AMDCPU implements CPU {//属性 名字private String name;//CPU的管脚数private int basePins;public AMDCPU(String name, int basePins) {this.name = name;this.basePins = basePins;}//CPU用户核心计算 实现一个接口 CPU 统一进行计算@Overridepublic void centerCalculate() {System.out.println("这是AMD的CPU" + this.name);}
}
InterCPU.java
public class InterCPU implements CPU{private String name;private int basePins;public InterCPU(String name,int basePins){this.name = name;this.basePins = basePins;}//CPU用户核心计算 实现一个接口 进行统一管理@Overridepublic void centerCalculate() {System.out.println("这是Inter的CPU" + this.name);}
}
CPU.java 统一管理的接口
void centerCalculate();
CPUFactory.java 创建对象的简单工厂
//默认 1 是 Inter 2 是 AMDpublic static CPU createCPU(int type){if(type == 1){return new InterCPU("Inter",1155);}else{return new AMDCPU("AMD",775);}}
ASUSMainBoard.java
private String name;//针对于CPU上面的管脚数private int CPUPins;public ASUSMainBoard(String name,int CPUPins){this.name = name;this.CPUPins = CPUPins;}//实现统一接口 安装主板@Overridepublic void installMainBoard() {System.out.println("ASUS主板"+this.name+",CPU针脚数为:"+this.CPUPins);}
MSIMainBoard.java
private String name;private int CPUPins;public MSIMainBoard(String name,int CPUPins){this.name = name;this.CPUPins = CPUPins;}//实现统一接口 安装主板@Overridepublic void installMainBoard() {System.out.println("MSI主板:"+this.name+",CPU针脚数为:"+this.CPUPins);}
MainBoard.java 统一接口
void installMainBoard();
MainBoardFactory.java 创建对象的简单工厂类
//传递参数:主板类型,默认 1 是 MSI 2 是 ASUSpublic static MainBoard createMainBoard(int type){if(type == 1){return new MSIMainBoard("MSI",1155);}else{return new ASUSMainBoard("ASUS",775);}}
TestMain.java
public static void main(String[] args){//用户根据需求向工程师要求用什么样的主板 什么样的CPUComputerEngineer engineer = new ComputerEngineer();engineer.assembleComputer(1,2);}
问题:这样让用户来传递主板和CPU的参数,可能会出现创建的对象和其他工厂的不匹配,比如创建了一个针脚为1155的主板,却创建了针脚为775的CPU,这样是不匹配的。而此时,抽象工厂模式,是更注重一系列对象互相之间的依赖关系。类似于资源的整合,同样针脚数的主板和CPU可以当做一套来创建返回给用户,这种方法的实现方式就是:使用一个抽象工厂来规定一套规则(主板和CPU),然后指定相应个计划,让用户来传递这个计划给工程师(相当于套餐),工程师组装电脑给用户。新增代码如下:
AbastactFactory.java
public interface AbstractFactory {MainBoard mainBoard();CPU CPU();
}
PlanA.java 装机方案A
/*** 一套计划————>实现抽象工厂类 为用户提供一套可用计划*/
public class PlanA implements AbstractFactory{@Overridepublic MainBoard mainBoard() {return new ASUSMainBoard("ASUS",1155);}@Overridepublic CPU CPU() {return new AMDCPU("AMD",1155);}
}
PlanB.java 和A类似 不予赘述
ComputerEngineer.java
//工程师根据用户提供的计划来组装电脑public void assembleComputer(AbstractFactory plan){//创建主板和CPUMainBoard board = plan.mainBoard();CPU cpu = plan.CPU();board.installMainBoard();cpu.centerCalculate();}
TestMain.java
public static void main(String[] args){//用户只需要提供一个方案 工程师按照方案来组装AbstractFactory plan = new PlanA();//找工程师ComputerEngineer engineer = new ComputerEngineer();engineer.assembleComputer(plan);}
策略模式
举个栗子:假设,假设哈,博主我是一个房地产开发商(美死我了)。那么我要卖房子了。客户来买房子,我们实行的是有认识的人可以打折的政策。假定用户认识:售楼部门主管,或者是帮我盖房子的工人,或者认识我。假定用户认识他们就可以享受打折。否则就是普通用户,无打折活动。那么下面的类来实现这一关系,售房。用户给一个指定价格来买对应的房子。
Price.java 报价的类
//报价的类
public class Price {//该方法用来计算用户买这个房的最终价格//参数:用户报价 和 是否具有享受活动的关系(使用int类型来代表关系)//0 代表 盖房子的工人 1 代表 售楼主管 2 代表 老板(我)//返回值:返回最终价格 由于买房涉及金额较大 直接使用int类型public int calculatePrice(int price,int identify){//判断是哪种身份来进行最终价格的计算switch(identify){case 0 :System.out.println("工人的亲属来买房啦,盖房子辛苦了,打个大折吧");return price - 100000;case 1 :System.out.println("认识我们售楼主管啊,多介绍亲戚来买房啊,给便宜些");return price - 50000;case 2 :System.out.println("认识老板啊,老板叫什么?看在老板面子上给你多便宜点吧");return price - 5;default:System.out.println("不是买房的啊,不能捣乱嗷");return price + 200000000;}}
}
直接new Price()对象调用方法即可。那么现在看这个实现方式,缺点是不是贼多。
首先:用户就迷惑了,你给的这个identify是啥意思啊,我该传个啥啊??不懂。。。可读性不好
其次:看看这方法里面,又是判断,又是计算的,累不累啊。。而且计算过程都暴露出来了,一点都不懂得保护隐私。。
好吧好吧。改写-->
传递参数identify时,可以换成一个静态常量,或者枚举也行。这样可读性会更好一些了。方法里面做的事情多呢,我就给他抽离出来呗,单独做一个方法你去调用。请看下面修改版:
Type.java 代表身份的类
public class Type {//静态常量 表示身份public static final int WORKER_PRICE = 0;public static final int MANAGER_PRICE = 1;public static final int BOSS_PRICE = 2;
}
改版后的price.java
//改版一:将身份参数换成静态常量 增加可读性//一个方法做事情太多,将一些事情抽出来单独形成一个方法public int calculatePrice(int price,int identify){int endPrice = 0;//判断是哪种身份来进行最终价格的计算switch(identify){case Type.WORKER_PRICE :endPrice = calculateForWorker(price);break;case Type.MANAGER_PRICE :endPrice = calculateForManager(price);break;case Type.BOSS_PRICE :endPrice = calculateForBoss(price);break;default:endPrice = calculateForDefault(price);break;}return endPrice;}//抽离出来用于计算价格的方法 参数:用户报价 返回值:返回计算出来的最终价格private int calculateForWorker(int price){System.out.println("工人的亲属来买房啦,盖房子辛苦了,打个大折吧");return price - 100000;}private int calculateForManager(int price){System.out.println("认识我们售楼主管啊,多介绍亲戚来买房啊,给便宜些");return price - 50000;}private int calculateForBoss(int price){System.out.println("认识老板啊,老板叫什么?看在老板面子上给你多便宜点吧");return price - 5;}private int calculateForDefault(int price){System.out.println("不是买房的啊,不能捣乱嗷");return price + 200000000;}
好啦,身份可读性增强了,计算方法也抽离出来了,还有什么可说的呢。当然有!!(毕竟今天的主题还没说呢是吧~.~)。先挑问题:目前是只有三个身份,倘若有一天有一百个身份,一万个身份,去添这个case,让谁添谁不得疯啊。正式一点的用语就是违反了设计模式的开闭原则。扩展性太差。这就引出了我们的策略模式。策略模式如果从代码结构上来分析就是将原有每一个单独计算的方法从类中拆出去,升级成为单独的类中的单独方法(每一个方法独立)。实现如下:
Stragegy.java 策略类接口 子类实现此接口,遵循此策略
public interface Strategy {int calculatePrice(int price);
}
各子类策略的实现
WorkerStrategy.java
/*** 工人的策略类 专门计算工人的买房价格*/
public class WorkerStrategy implements Strategy{@Overridepublic int calculatePrice(int price) {System.out.println("工人的亲属来买房啦,盖房子辛苦了,打个大折吧");return price - 100000;}
}
ManagerStrategy.java
/*** 售楼主管的策略类*/
public class ManagerStrategy implements Strategy{@Overridepublic int calculatePrice(int price) {System.out.println("认识我们售楼主管啊,多介绍亲戚来买房啊,给便宜些");return price - 50000;}
}
BossStrategy.java同上,不赘述。
Price.java:
//得到价格的方法//参数:价格 父类的策略public int getPrice(int price,Strategy strategy){int endPrice = 0;endPrice = strategy.calculatePrice(price);return endPrice;}
策略模式未来就是来解决这种流程固定,但是具体执行有些差别的问题。
观察者模式
举个栗子:假设有一家甜品店。不定期推出新的甜品试吃活动。有试吃活动肯定会有试吃用户,则用户和甜品店之间就是 依赖关系(use - a:一个类中的一个方法使用到另一个类对象)甜品店需要有用户来试吃 。先来实现普通的甜品店和试吃用户的类之间的关系。
DessertShop.java 甜品店类
/*** 甜品店类*/
public class DessertShop {//属性private String name;/*getter和setter方法省略*///出新品的方法 用户来试吃//参数:不需要//返回值:新甜品public Dessert newDessert(){System.out.println("新甜品出来啦,快来免费试吃啊");Dessert dessert = new Dessert();dessert.setName("牛奶小新");return dessert;}
}
Dessert.java 甜品类
/***甜品类*/
public class Dessert {private String name;/*getter和setter方法省略*/
}
EatPerson1.java 试吃员1号(不是吃人1号啊)
/*** 1号试吃员*/
public class EatPerson1 {private String name;public EatPerson1(String name){this.name = name;}//获取新的甜品//参数:甜品店 返回值:不需要public void eatNew(DessertShop shop){Dessert dessert = new Dessert();System.out.println("试吃了"+shop.getName()+"家的"+dessert.getName()+"甜品");}
}
TestMain.java
public class TestMain {public static void main(String[] args){//创建甜品店对象DessertShop shop = new DessertShop("1号甜品店");//创建试吃用户EatPerson1 person1 = new EatPerson1("1号试吃员");//用户主动找到这个甜品店person1.eatNew(shop);}
}
好,那么我们看这个普通的类设计有什么问题呢,到底是什么样的问题才能引入今天的主题来呢?
首先:现在只有一个对象,但是保不齐某一天甜品店爆火,试吃人数激增呢,还去创建这么些对象吗??但是这个问题目前是无法解决的,但是在后期Spring的IOC就可以来做到了,所以这里暂时不去他做专门的处理。
其次:每次试吃让客户自己主动,这样对象一多,做的事情就多了,是不是不太好啊,而且,我不要面子的嘛!!
好,遇到问题,解决问题。引出今天的主题-->观察者模式。谁观察?? 大家想一下公众号。是不是公众号只要有新文章就会推送给用户啊,那么我们用户就充当了一个观察者的角色,来监听着甜品店的动向,什么时候出新品,我要吃热乎的!!!
好,那我们来改变一下类的设计:这个时候就要从依赖关系变成了 has - a的聚合关系。甜品店中存储了很多用户,来监听着甜品店的动向。
基本流程:
1. 目标对象中存储这很多观察者
一个属性,集合,里面存储这很多的观察者
2.设计一个方法
往集合中添加/删除观察者
3.甜品店做事情
4.甜品店通知每一位用户来试吃甜品
代码实现如下:
EatPerson.java 试吃员统一接口
/*** 试吃员统一接口*/
public interface EatPerson {void eatNew(DessertShop shop);
}
EatPerson1.java EatPerson2.java EatPerson3.java 都实现此接口,代码未做改变。
DessertShop.java 改版后的甜品店类
/*** 甜品店类*/
public class DessertShop {//属性private String name;/*getter 和 setter方法省略*///=====新版本===================================================//需要一个集合来存储所有的观察者//观察者需要一个统一的规范,来当做我存储的泛型类 接口//为什么使用ArrayList??因为这个集合遍历操作使用的比较多private List<EatPerson> observers = new ArrayList<>();//将观察者放入到集合中public void add(EatPerson person){observers.add(person);}//可能会移除观察者public void remove(EatPerson person){observers.remove(person);}//通知所有的观察者public void notity(){for(EatPerson p : observers){p.eatNew(this);}}//=====旧版本==================================================//出新品的方法 用户来试吃//参数:不需要//返回值:新甜品public void newDessert(){System.out.println("新甜品出来啦,快来免费试吃啊");Dessert dessert = new Dessert();dessert.setName("牛奶小新");//通知每个观察者this.notity();}
}
TestMain.java 测试方法
public static void main(String[] args){//===新版本========================================//创建甜品店对象DessertShop shop = new DessertShop("1号甜品店");//创建客户对象EatPerson1 person1 = new EatPerson1("1号试吃员");EatPerson2 person2 = new EatPerson2("2号试吃员");EatPerson3 person3 = new EatPerson3("3号试吃员");//甜品店主动 找客户//将客户装到观察者集合中shop.add(person1);shop.add(person2);shop.add(person3);//找用户试吃shop.newDessert();}
以上便是观察者模式。
适配器模式
适配器模式可将其划分为三类(也就是此文中要介绍的三种):
1.对象适配器
2.类适配器
3.缺省适配器
先来介绍对象适配器。为了更加贴合,就使用电脑上面的存储卡(SD)卡来当做此模式的示例来实现吧。我们先来介绍对象适配器。
众所周知,存储卡是分为大卡和小卡的,电脑使用的就是正常大卡,我们的电脑上面也仅仅只留了一个接口用来插存储卡,那我要是想使用小卡插入到电脑中该怎么办呢?自己拿锥子开辟一个接口??那电脑凿成什么样子了。小玩笑(一点也不好笑)。相比同志们也都见过卡套吧,一个小卡外面套着一个大卡模型的模样(将自己打扮成大人模样是吧)。外面那一层是没有芯片的,只是充当一个模具来将小卡送到卡槽中。那这样一个卡套就是我们今天要说的“对象适配器”了。详情见如下代码实现:
对象适配器
假定以金士顿品牌的SD卡来当做案例。分为大卡,小卡
MiniSDCard.java 小卡统一接口
/*** 小卡统一接口*/
public interface MiniSDCard {public void read();public void write();
}
SDCard.java 大卡统一接口
/*** 存储卡统一读写数据的接口*/
public interface SDCard {public void read();public void write();
}
KingstonMiniSDCard.java 金士顿小SD卡
/*** 金士顿牌子的小卡* 实现统一存储卡小卡接口*/
public class KingstonMiniSDCard implements MiniSDCard {//读取和写入的功能public void read(){System.out.println("小卡的读取功能");}public void write(){System.out.println("小卡的写入功能");}
}
KingstonSDCard.java 金士顿大SD卡 和小卡实现一致 不赘述
CoverForMiniSDCard.java 小卡卡套
/*** 小卡的卡套 将小卡包到卡套里面之后就是一个大卡 所以实现统一大卡接口*/
public class CoverForMiniSDCard implements SDCard {//卡套和小卡的关系??//卡套和小卡形成了一个整体 没有卡套 小卡没法使用 没有小卡 卡套只是一个塑料//所以二者在类上是has - a的包含关系private MiniSDCard miniSDCard;public CoverForMiniSDCard(MiniSDCard miniSDCard){this.miniSDCard = miniSDCard;}//在读取和写入数据的时候 当然使用小卡来操作 因为芯片在小卡里面 卡套只是一个工具@Overridepublic void read() {this.miniSDCard.read();}@Overridepublic void write() {this.miniSDCard.write();}
}
Computer.java 插卡的电脑
/*** 插存储卡的电脑*/
public class Computer {//电脑的读取和写入//参数:给电脑一张卡 只需要传递统一大卡接口 因为小卡已经使用卡套变成了大卡样子public void readAndWrite(SDCard sdCard){sdCard.read();sdCard.write();}
}
TestMain.java 对象适配器测试类
public static void main(String[] args){//先需要一个电脑Computer computer = new Computer();//需要一张大卡 本来的大卡可以使用 所以只需要操作小卡//小卡需要卡套 所以先new小卡 有了小卡 传递给卡套 形成了大卡 传递给电脑 读取和写入数据成功MiniSDCard mini = new KingstonMiniSDCard();CoverForMiniSDCard cover = new CoverForMiniSDCard(mini);computer.readAndWrite(cover);}
类适配器
假设有一种小卡,是可以折叠的,展开是一张大卡,一半是芯片,另一个半是塑料。我们再用这样一种卡来实现匹配类适配器。
新增类
SpecialSDCardForMini.java 折叠小卡 展开是一张大卡 内含芯片
/*** 可以折叠的一种小卡* 首先,他是金士顿的一种卡 extends 体现是一个* 其次,他需要当做大卡使用 遵循统一大卡的规则*/
public class SpecialSDCardForMini extends KingstonMiniSDCard implements SDCard {@Overridepublic void read() {this.readMini();}@Overridepublic void write() {this.writeMini();}
}
TestMain.java
public static void main(String[] args){//先要有一个电脑Computer computer = new Computer();//需要一张可折叠的卡SDCard card = new SpecialSDCardForMini();computer.readAndWrite(card);}
其他类无变化。仅添加了折叠卡类和去除了对于卡套的使用。
实际底层就是对于小卡变成大卡的适配实现。
缺省适配器
缺省适配器就是相当于电脑上面的一个扩展坞,由扩展坞直接和电脑连接,其他的实现类去和扩展坞连接就可以(在扩展坞中选择自己需要的方法,进行方法重写)。
USBCard.java 缺省适配器抽象类,自身实现了大卡和小卡的接口,具有通用的方法,用户可以在其中选择需要的方法进行重写
/*** 抽象类 扩展坞*/
public abstract class USBCard implements MiniSDCard, SDCard {@Overridepublic void readMini(){System.out.println("小卡的读功能");}@Overridepublic void writeMini(){System.out.println("小卡的写功能");}@Overridepublic void read(){System.out.println("大卡的读功能");}@Overridepublic void write(){System.out.println("大卡的写功能");}
}
KingstonSDCard.java 大卡继承拓展坞
/*** 金士顿牌子的正常卡(电脑使用的大卡)* 继承拓展坞类 重写需要的方法*/
public class KingstonSDCard extends USBCard {//有读取功能public void read(){System.out.println("大卡读取");}//有写入的功能public void write(){System.out.println("大卡写入");}
}
KingstonMiniCard.java 小卡继承拓展坞
/*** 金士顿牌子的小卡* 继承拓展坞类 实现需要的方法*/
public class KingstonMiniSDCard extends USBCard {//读取和写入的功能public void readMini(){System.out.println("小卡的读取功能");}public void writeMini(){System.out.println("小卡的写入功能");}
}
以上就是适配器模式的三种情况。
模板方法模式(模板模式)
话不多说,上来就干!我们今天来做一个小登录(玩点小高级的东西)。通过实现这个登录方法来引出我们今天的主题--模板方法模式。既然是登录嘛,主题是设计模式,所以一切从简,数据库啥的就不写了,直接来个简易版。那也就是一个实体类(存储用户名密码),一个普通用户类,一个管理员类,一个测试类。齐活。等等,代码附在后面。
AdminUser.java 存储管理员用户名和密码的实体类
private String username;
private String password;
/*其余略。。。*/
NormalUser.java 存储普通用户的用户名和密码类
private String username;
private String password;
/*其余略。。。*/
AdminUserLogin.java 管理员的登录方法
//管理员登录方法public String adminLogin(String username,String password){//假装从dao中查询admin的信息AdminUser admin = this.selectOne(username);//获取加密的密码String encryptPass = this.encryptPassword(password);if(admin != null && encryptPass.equals(admin.getPassword())){return "登录成功";}return "登录失败,用户名或密码错误";}//dao查询admin的信息public AdminUser selectOne(String username){AdminUser admin = new AdminUser("Java","666");//给管理员的密码加个密 后存入到"数据库"中this.encryptPassword(admin.getPassword());return admin;}//加密方法 给管理员的密码加密public String encryptPassword(String password){System.out.println("给管理员的密码加密了");return password;}
NormalUserLogin.java 普通用户的登录方法
//普通用户登录方法public String normalLogin(String username,String password){//调用dao方法(假装)通过username查询信息 判断是否登录成功NormalUser user = this.selectOne(username);if(!user.equals(null) && password.equals(user.getPassword())){return "登录成功";}return "登录失败,用户名或密码错误";}//假装从数据库中查询信息的daopublic NormalUser selectOne(String username){//先来一个NormalUserNormalUser user = new NormalUser("炸鸡","123");return user;}
好啦。这样我们定睛一看!问题这不就来了嘛。这样每一个用户,这样来看仅仅是一个类就搞定了信息的存储,但是如果放到数据库中,这可就是一个表啊,一个管理员一个用户这就两张表了。而且这两个类的方法都类似,登录方法,实体类都是一致的。是否可以将这两个合并到一起呢。当然。这也就是今天的模板方法模式的主场了。请看以下操作。
User.java 普通用户和管理员共用的实体类
private String username;private String password;//好 我们假设用户又多了一个属性 age 管理员在赋值的时候可直接赋值为空private Integer age;
LoginTemplate.java 登录模板抽象类 子类继承去实现登录等一些登录相关操作
/*** 将用户和管理员相同的方法都放到这个抽象类里面 来当做一个模板* 因为抽象类是允许有非抽象方法 和 抽象方法的* 所以 保不齐有方法两者都需要 且 操作不同 那么这个方法就当做必须重写的方法定义到这个类里面*/
public abstract class LoginTemplate {//方法一:登录验证流程 这个就当做是一个统一的 不需要重写//同时 我们也默认这个方法是不允许子类去重写的 因为这个流程我已经规定好了 你不用改//所以我要用final来修饰这个方法 以防子类去重写来打乱我执行的流程public final String login(String username,String password){//调用管理员和普通用户各自的查询信息的方法 去获取对象信息User user = this.selectOne(username);//但是管理员的密码是有一个加密需求的 所以需要规定一个密码加密方法 但是不是必须重写的 因为普通用户不需要String encryptPass = this.encryptPassword(password);//判断密码是否正确if(user != null && password.equals(encryptPass)){return "登录成功";}return "登录失败,用户名或密码错误";}//查询对象信息验证登录是否正确//虽然这个方法两个类都需要 但是流程又不一样 那么好,我做一个定义 你去自己重写好了//这个方法我只允许子类实现 但是用public修饰时,在同包中都是可见的 所以需要将其保护起来protected abstract User selectOne(String username);//密码加密方法 不是必须重写 谁需要谁重写protected String encryptPassword(String password){//这个方法在这里面什么也不做 等着子类去重写return password;}
}
AdminLogin.java 管理员登录
/*** 管理员登录 继承抽象模板方法 重写查询方法 和 密码加密方法*/
public class AdminLogin extends LoginTemplate{@Overridepublic User selectOne(String username) {User user = new User();user.setUsername("炸鸡");user.setPassword("123");return user;}@Overrideprotected String encryptPassword(String password) {System.out.println("密码加密咯");return password;}}
NormalLogin.java 普通用户登录
/*** 普通用户登录 继承模板方法 实现查询方法 不需要实现加密方法*/
public class NormalLogin extends LoginTemplate{@Overridepublic User selectOne(String username) {User user = new User();user.setUsername("Java");user.setPassword("666");return user;}
}
好啦。这就是模板模式啦,您觉得怎样呢
装饰者模式
首先。举个栗子,假设现在有一个卖房子的团队,那我们实现一下关于他们工资发放的流程吧(简单的流程,因为我也不懂具体是怎么发放)。该团队肯定有普通职员,工作年限为半年,假设其销售额度为4000,每个月绩效提成为3%;另有一位普通职员,工作年限为3年,假设其销售额度为6000,每个月绩效提成为3%,销售额度每大于5000再在其基础上涨幅1%;还有团队负责人(主管),假设其销售为7000,每个月绩效提成为3%,但是还会提取超过销售额度超过5000的1%,以及团队总额的1%。下面是普通的实现,不含设计模式。
User.java 描述职员信息
//姓名private String name;//工资private double salary;//身份 普通/主管private String identity;
UserBox.java 替代数据库,存储职员信息
public class UserBox {//使用一个map集合private static HashMap<String,User> userBox = new HashMap<>();static{userBox.put("Ming",new User("小明",4000,"普通"));userBox.put("Gang",new User("小刚",6000,"普通"));userBox.put("Hong",new User("小红",7000,"主管"));}//设计一个方法 获取集合public static HashMap<String,User> getUserBox(){return userBox;}//设计一个方法 通过名字获取对应的人信息public static User getUser(String user){return userBox.get(user);}
}
CalcBonus.java 计算绩效奖金
public class CalcBonus {//通过给定的名字 来计算其对应的奖金public double calcBonus(String name){double sum = 0;//分别找三个小弟 对三种奖金制度进行分别的计算//操作一:计算基础奖金sum += baseBonus(name);//操作二:计算销售额大于5000的奖金sum += beyondBonus(name);//操作三:计算主管要提取的sum += extraBonus(name);return sum;}//小弟一:计算基础奖金private double baseBonus(String name){User user = UserBox.getUser(name);double sum = 0;sum = user.getSalary() * 0.03;return sum;}//小弟二:计算销售额大于5000的奖金private double beyondBonus(String name){User user = UserBox.getUser(name);double sum = 0;double salary = user.getSalary();if(salary >= 5000){sum = salary * 0.01;}return sum;}//小弟三:计算主管提取的奖金private double extraBonus(String name){double sum = 0;//查看当前name是否为主管if("主管".equals(name)){// 遍历所有职员double all = 0;for(String key : UserBox.getUserBox().keySet()){//将所有职员的工资加起来 然后乘以0.01 就是主管要提取的团队奖金all += UserBox.getUser(key).getSalary();}//提取sum = all * 0.01;}return sum;}
}
这是普通的不带有设计模式的一个实现方式,缺点是很明显的--->可扩展性极差,也就是说如果我可能在某一天改变奖金策略,或者不发奖金,这样修改起来就很麻烦,而且如果我要新增一个奖金政策,还需要去CaluBonus类中新增一个方法去计算对应的计算策略,那万一我已经将项目打包呢,是否又违反了开闭原则。there is problem,there is slove(塑料英语,别介意)。装饰者模式来咯。我们可以把装饰者模式想象成买煎饼的场面。首先肯定是一张饼躺在那,上面才能"装饰"火腿肠啊,鸡柳啊,生菜啊等等的。那我们来简单的实现一下。(每个步骤会在代码中有较为详细的注释)。
从表层到底层的次序来展示代码:
Bonus.java 基础的奖金类
/*** 最基础的计算奖金的类 相当于煎饼中的一个饼*/
/*** 将每个装饰者都实现了Decorator抽象类 那么现在 我当前这个类和Decorator又是什么关系呢,* 现在是我写的方法名一致 可能知道他们是计算奖金的 但是如果方法不一致呢,而且用户也不知道啊* 那不如再来一个超级父类 将他俩绑定到一起 产生一个装饰关系 用接口定义一个规则 用户用起来也方便 SuperBonus.java*/
public class Bonus implements SuperBonus{//设计一个方法 用来计算奖金public double calcuBonus(String name){//老板心情不好 奖金全部取消!! 好了,这个方法没有奖金而言System.out.println("默认没有奖金");double num = 0;return num;}
}
MonthBonus.java 装饰者1 月度基本奖金
/*** 每个月的基本的0.03奖金* 在Bonus之后 获取该类的奖金* 相当于煎饼中的一个火腿肠-->是额外加的*/
public class MonthBonus extends Decorator{//直接继承装饰者类private Decorator d;public MonthBonus(Decorator d){this.d = d;}
// private Bonus bonus;public double calcuBonus(String name){//先获得bonusdouble sum = d.calcuBonus(name);//计算自己的sum += UserBox.getUser(name).getSalary() * 0.03;return sum;}
}
BeyondBonus.java 装饰者2 销售额超额奖金
/*** 销售额度超过5000块的1%的提成(装饰者1)* 会在Bonus之后才有 先有基础的 再有外加的* 相当于煎饼中的鸡柳-->额外的*/
public class BeyondBonus extends Decorator{//直接继承装饰者对象
// private Bonus bonus;
// private MonthBonus bonus;private Decorator d;public BeyondBonus(Decorator d){this.d = d;}public double calcuBonus(String name){//先获得基础的奖金(管你有没有,先要再说)double sum = d.calcuBonus(name);//计算我自己辛苦得来的那1%奖金sum += UserBox.getUser(name).getSalary() * 0.01;return sum;}
}
ExtraBonus.java 装饰者3 主管提取奖金
/*** 主管提取全部职员的总额的1%的奖金类* 前提是:我先要基础的 再要销售额大于5000的那1% 再计算我自己的* 相当于煎饼中的 鸡排-->额外的*/
public class ExtraBonus extends Decorator{private Decorator d;//构造方法 创建对象public ExtraBonus(Decorator d){this.d = d;}//既然都是装饰者 何不如归为一个类下-->继承抽象类 Decorator
// private Bonus bonus;
// private BeyondBonus beyond;public double calcuBonus(String name){//基础奖金double sum = d.calcuBonus(name);//5000块的1%提成sum += d.calcuBonus(name);//计算我自己的总额1%//遍历mapdouble all = 0;for(String key : UserBox.getUserBox().keySet()){all += UserBox.getUser(key).getSalary();}sum += all * 0.01;return sum;}
}
Decorator.java 抽象类 规定装饰者的模板
/*** 装饰者的父类 规定一个装饰者的模板* 实现统一计算奖金的接口 统一方法名*/
public abstract class Decorator implements SuperBonus{//计算奖金的抽象方法public abstract double calcuBonus(String name);
}
SuperBonus.java 接口 统一Bonus和Decorator方法名
/*** 声明Bonus和Decorator的关系 让计算奖金的方法名统一 用户用起来较为方便*/
public interface SuperBonus {public double calcuBonus(String name);
}
装饰者模式到此结束~~
静态代理模式
同样的,举个栗子先。今天我们来举关于手机的例子。手机,当前要工厂了,假设,假设一下,现在的手机售卖店都是前面是门店,后面是工厂,当然,工厂是隐藏起来的。就举iPhone的例子吧,一个手机店配着一个工厂,那么手机店和工厂就是use-a的依赖关系啦。好,那我们开始实现。老样子,先实现最基础的类和类的关系,然后一点点增加,直到形成设计模式。
iPhone.java 描述手机
/*** 这个类是用来描述手机的*/
public class iPhone {private String name;private String color;/*构造方法略*///手机的一些功能public void game(){System.out.println("苹果手机打游戏反应快");}public void call(){System.out.println("苹果手机打电话流畅");}public void photo(){System.out.println("苹果手机拍照清晰");}
}
AppleStore.java 手机店(包含工厂)
/*** 这个类是苹果手机的专卖店 可以理解为他的工厂*/
private class AppleStore {//这个手机店因为带着一个工厂 所以他可以生产手机和卖手机public iPhone productPhone(){iPhone phone = new iPhone("iPhone11","white");System.out.println("生产一台手机");return phone;}public iPhone salePhone(){iPhone phone = this.productPhone();System.out.println("售卖手机");return phone;}
}
测试方法直接创建手机店,卖手机即可,这里不在描述。
那现在有这么一个情况,人们都知道这个手机好卖,那么就有商人也想卖这种牌子的手机,于是申请为代理商(这个代理卖真机的过程不再描述,重点在后面),代理商卖了一段时间之后,用户信誉已经养成差不多了,开始觉得里面似乎有利可图,可以将采购回来的真机里面的零部件换上那么一两个,反正一般的客户也看不出来。这样的事情愈演愈烈,终于有一天,整部手机都变成了假的。当全部都变成假的了的时候,还有必要从人家那里拿货自己再去处理吗,那也太麻烦了,有那时间早就自己造一个工厂了。对!自己来一个山寨工厂,造山寨手机。然后拿来当做真机卖。但是用户不知道啊,还以为代理商卖的是真手机,还来这买,殊不知已经被偷偷地换过了。
经过一番收拾,把代理商门店装修的本店一模一样,连客服都穿一样的衣服,然后再将手机变成和真机一样的外观,一样的功能方法。用户在购买的时候就无法分清楚所拿到的是真手机还是假手机。
ProxyStore.java 代理商
/*** 代理商 实现Store接口 装修风格和真手机店一模一样 并且要一样的卖手机的方式和客服*/
public class ProxyStore implements Store{//代理商全部卖假手机 自己造一个山寨工厂 生成rPhoneprivate rPhone productPhone(){rPhone phone = new rPhone("rPhone11","white");return phone;}//售卖rPhone@Overridepublic rPhone salePhone(){//也可以售卖真手机 这个可以根据自己设计来选择(遇到漂亮的小姐姐就卖假的,制造售后机会)rPhone phone = this.productPhone();System.out.println("卖假手机咯");return phone;}
}
rPhone.java 假手机
/*** 用来描述假手机 仿真iPhone 和真iphone有一样的功能*/
public class rPhone implements Phone{private String name;private String color;public rPhone(String name,String color){this.name = name;this.color = color;}public void call(){System.out.println("假iPhone,打电话,你说什么,我听不清");}public void game(){System.out.println("假iPhone,打游戏,闪退");}public void photo(){System.out.println("假iPhone,拍照,自动打码");}
}
Phone.java 统一手机接口 长得一样 功能方法也一样
/*** 统一手机接口 长得一样 功能一样*/
public interface Phone {public void game();public void photo();public void call();
}
Store.java
/*** 统一手机店的装修风格 看不出来真假 实现一个卖手机的方法 连客服都一样*/
public interface Store {public Phone salePhone();
}
静态代理模式一般在进行架构的时候可能会使用上,因为这样在需要更换一些已实现的功能或底层架构的时候不会对使用者造成影响。(偷偷地)
动态代理模式
这次要介绍的动态代理模式就以静态代理模式例子为基础来讲吧,这样比较好理解一些。代码实现依然保持静态代理的形式,保留了:Store.class,iPhone.class,AppleStore.class三个类,只是排除了静态代理里面的代理商。这次的代理商我不想自己去创建了,我想让别人创建好给我,我只需要拿着这个创建好的代理对象去卖手机即可。那么我这个“别人”应该是谁呢,很明显,就是一个工厂嘛,就是我这个手机的代工厂,我把手机的规格,各种配置啥的给你,你按照这个要求去生产,然后给我,我拿去卖,这样多好啊。
StoreFactory.java 代理工厂类 帮我做手机
/*** 这个类就相当于是一个代工厂 用来创建实例对象 做一些这个对象应该做的事情,返回出去一个用户需要的结果*/
public class StoreFactory {//可能会有不同的人来找我做事(我做的事情肯定不是单一的生成iPhone手机,肯定还能干其他事情)//所以对于不同的用户调用可能要返回不同的代理对象 泛型//参数呢? 用户给我一个需要代理的对象(给我一个需要去代理的对象)public static <T>T getInstance(Class clazz){//创建代理类的对象是java提供的工具方法//参数:三个参数://1.类加载器 把需要代理的这个对象加载进来ClassLoader loader = clazz.getClassLoader();//2.代理谁 将要代理的对象包成一个数组传递Class[] classes = new Class[]{clazz};//3.代理之后我需要做什么事情InvocationHandler handler = new InvocationHandler() {@Override//这个方法就是代理这个对象之后我改做什么事情public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是代工厂,我要生产手机了");iPhone phone = new iPhone("iPhone11","white");return phone;}};T proxy = (T) Proxy.newProxyInstance(loader, classes,handler);//将这个代理的对象返回出去return proxy;}
}
由于事情都交给了代理工厂来做,AppleStore只是规定代理工厂要做什么,所以将其抽象成为一个接口。
AppleStore.java
/*** 这个类是苹果手机的专卖店 可以理解为他的工厂 实现统一手机店接口*/
public interface AppleStore extends Store {@Overridepublic iPhone salePhone() ;
}
测试类 通过代理工厂来获得手机对象并售卖出去
public static void main(String[] args) {//找代理工厂 来帮我生产手机 我要卖手机啦//但是在这个AppleStore里面 除了一个卖手机的方法之外已经不做任何其他事情了 事情都交给了代理工厂来做//所以需要将其抽象为一个接口 (只需要告诉代工厂你这个手机的型号之类的信息即可)AppleStore store = StoreFactory.getInstance(AppleStore.class);iPhone phone = store.salePhone();//使用手机phone.call();phone.game();phone.game();
}
以上就是动态代理模式了,我自己对于动态代理的理解就是,对于代理对象的动态,对于不能的代理对象会做不同的事情,而不是只能卖手机,就像是一个代工厂,不一定只能做一个产品。谁来找我做我可以干的事情,我都干。就是这样一个动态的实现。
责任链模式
责任链模式在比较多的领域中如tomcat,Spring等中都有使用。个人觉得理解起来还是比较简单的。同样的,例子先行。由于和链相关,那咱就用链表来引出这个模式的问题。LinkedList想必大家都知道。我找你,你找他嘛(山有木兮木有枝~.~)。由于大家可能对于链表比较熟悉了,这里就不做代码实现了。我们只用链表来引出责任链模式要解决的问题。大家都知道,链表是有头结点的。那么为了便于理解,我们暂且把这个头结点比作是一位老师,后面的其他结点都是一班里面的同学。然后这个连接的过程我们把他比作老师喊同学回答问题的过程(怎么样,很形象吧)。那么放在链表上这样的关系就是,老师先找一个学生,然后学生再找下一个学生。看起来好像挺好的,老师只需要找一次,剩下的同学都会自动去回答。但是呢,这样下来,每次提问问题都这样,那坐在后排的同学岂不是太爽了,因为有充足的时间去思考这个问题,但这似乎违背了老师想传授的一个对于知识快速做出反应的能力。所以这问题也就很明显了。那么我们只好让老师一个一个的去找学生了,那么老师找了一个学生之后,什么时候开始找下一个学生呢?——>当前这个同学回答完毕之后给老师一个反馈,老师得到这个反馈之后,继续找下一个同学。大家看这个方法是不是很好呢。那么这也就是责任链模式了。下面是代码实现以及较为详细的注释讲解。
ApplicationChain.java 相当于教师类
/*** 这个类好比是老师 用来找同学回答问题*/
public class ApplicationChain {//记录当前提问到哪个同学(保证不能重复)private int counts = 0;//老师这个班级里面肯定要有学生啊//用一个集合 来存储所有的学生private ArrayList<Node> nodes = new ArrayList<>();//提供一个方法 将学生交给老师管理public void addNode(Node node){nodes.add(node);}//老师喊同学回答问题//同学在哪?? 集合里面public void doFilter(){//从集合里随机获取一个 暂不考虑越界的问题(主要是设计模式)Node node = nodes.get(counts++);//找同学回答问题node.doFilter(this);}
}
Node.java 相当于学生类
/*** 这个类好比是每个同学 在一个班级里面*/
public class Node {//学生的属性private int id;public Node(int id){this.id = id;}//回答问题的方法//给谁回答呢?老师//那这个老师我从哪获得呢?//属性? 学生有一个老师?不合适吧//传参? 每个同学有一个老师?多个同学可以对应同一个老师 可以public void doFilter(ApplicationChain chain){//回答问题System.out.println(this.id+"号同学回答问题");//问题回答完毕了 干什么??//告诉老师 我回答完了,你可以找下一个同学了 我自己不需要知道下一个是谁//我自己问题回答对了,自己偷着乐,回答错了反省去,别管下一个是谁//找老师chain.doFilter();}
}
TestMain.java 测试类
public static void main(String[] args) {//创建学生类Node node1 = new Node(1);Node node2 = new Node(2);Node node3 = new Node(3);//创建老师类ApplicationChain chain = new ApplicationChain();//将学生交给老师管理chain.addNode(node1);chain.addNode(node2);chain.addNode(node3);//找学生回答问题chain.doFilter();}
以上就是责任链模式了,个人觉得理解起来还是比较简单的。
享元模式
所谓享元模式:享-->共享 元-->单元(对象) ,是一种结构型的设计模式,主要就是为了减少对象的创建(和单例模式是有区别的)。
同样的,我们用一个例子来引出需要介绍的内容。就举一个学生去图书馆借书的例子吧。那么在这个例子的基础上,会产生出三个对象:图书馆、书、学生。图书馆里面有书,学生可以使用图书馆来实现借书操作。图书馆默认每个学校只有一个,所以需要将其变成一份(单例的)。
最简单的享元模型:图书馆中的每一类书都只有一本,每次学生来借这个书,都会创建一个新的当前书对象(相当于图书馆会每次买一本新的这一类的书),但是考虑到学生会归还书籍的,当所有的书都归还回来极有可能导致图书馆放不下。那么就考虑到在图书馆中引入书柜,将同一名字的书多放几本,然后供多个学生循环使用。
改版后的享元模型:图书馆中引入了书柜(一个集合,用来存储同一名字的书籍),将同一名字的书存多份,并在初始化时为每一本书设置一个借阅状态(boolean),这样同学们就可以共享这几本书,而不至于每次都去买新的。这就达到了对象的共享,而不是每次都去创建新的。学生每次取图书馆借书,图书馆为学生发放对应的书籍(或是人工,或是自动),而不是学生自己去取,也就将将书对象隐藏到图书馆里面,学生是看不到的,所以图书馆从书柜拿书的过程学生也是不知道的,学生只负责拿到书去看,看完归还,剩下的操作都是图书馆来做的。
享元模式整体的思想也就是:尽量少的去减少同一类型对象的创建,能复用尽量复用,从而减少内存空间的浪费,同单例模式不同,单例模式是将对象变成一份的,大家共享;而享元模式是将对象变成多份大家轮询共享。
(以上享元模式的总结为本人学习之后的个人理解)
迭代器模式
这里的迭代器模式是和java.util.Iterator对象是一样的。就是在遍历时有一个统一的规则,方便于遍历操作。这里假设有一个餐馆,专门用来做快餐,有自己的服务员,有自己的菜单。在某一天,这家餐馆里面入驻了一家蛋糕店。这家蛋糕店也有自己的菜单,但是由于店面太小,没有服务员,就把请服务员的钱给到快餐店,让快餐店来 帮忙售卖自己家的产品。那么这个时候服务员就需要每来一位顾客就要拿着两个菜单给顾客点菜。这样对于服务员来说是很麻烦的。所以我们希望有一个统一的规则来将这两个菜单合并到一起(将所有的菜提供给顾客,自己选择)。这就需要要求两家店面都实现一个接口(自定义的 Iteraotor)。这样每个店面在初始化的时候将菜单构造出来,然后将其放入到一个菜单集合中,当两个门店都这样做了之后,我两个门店的菜单就交到迭代器手上了。然后服务员通过获取两个门店的迭代器对象(菜单),开始自己的遍历,将所有菜单全部展示出来。
以下是代码实现:
Iterator.java 自定义的迭代器统一规则
boolean hasNext();
Object next();
MenuItem.java 描述菜品
private String name;//名称private String description;//菜品描述private double price;//价格
Restaurant.java 快餐店 用于初始化菜单
public DinerMenu() {menuItems = new MenuItem[MAX_ITEMS];//4个长度//这里面添加本店的菜品}//为了让服务员点菜方便 将菜单全部拿走public MenuItem[] getMenuItems() {return menuItems;}//提供一个获取迭代器对象的集合public Iterator createIterator(){return new DinerMenuIterator(menuItems);}
RestaurantInterator.java 实现迭代器规则的快餐店
public class RestaurantInteratorimplements Iterator{private MenuItem[] items;private int position = 0;//迭代位置public RestaurantInterator(MenuItem[] items) {this.items = items;}public boolean hasNext() {if(position >= items.length || items[position] == null) {return false;}else {return true;}}public Object next() {MenuItem menuItem = items[position++];return menuItem;}
}
Cake.java 入驻的蛋糕店,实现方式和餐馆一致,不赘述
Waiter.java 服务员类 获取两个门店的迭代器对象,遍历两个店的菜单
public void printMenu(){//获取两个迭代器对象Iterator cit = cake.createIterator();Iterator rit = restaurant.createIterator();//让小弟帮我们迭代this.myprint(cit);this.myprint(rit);}//设计一个小弟方法 迭代private void myprint(Iterator iterator){while(iterator.hasNext()){MenuItem menuItem = (MenuItem) iterator.next();System.out.print(menuItem.getName()+", ");System.out.print(menuItem.getPrice()+", ");System.out.println(menuItem.getDescription());}}
创作不易,辛苦点个赞吧~~
大话设计模式(更新ing...)相关推荐
- 《大话设计模式》第29章-OOTV杯超级模式大赛—模式总结(四)
<大话设计模式>将于11月底由清华大学出版社出版 <大话设计模式>第29章-OOTV杯超级模式大赛-模式总结(一) <大话设计模式>第29章-OOTV杯超级模式大赛 ...
- 大话设计模式Python实现-观察者模式
观察者模式(发布-订阅模式 Publish Subscribe Pattern):定义了一种一对多的关系,让多个观察对象同时监听一个主题对象,当主题对象状态发生变化时会通知所有观察者,是它们能够自动更 ...
- 大话设计模式—观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).比如,当一个对象被修改时,则会自动通知它的依赖对象.观察者模式属于行为型模式.主要解决一个对象状态改变给其他对象通知的问 ...
- 大话设计模式—原型模式
原型模式(Prototype Pattern),用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象:即用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建 ...
- 大话设计模式读书笔记
主题 概要 设计模式 大话设计模式读书笔记 编辑 时间 新建 20170423 序号 参考资料 1 大话设计模式 重新看了一遍设计模式,除了一些已经特别熟悉的模式,都自己敲了一遍代码,有些豁然开朗的感 ...
- Effective C++ 中文版(第三版)读书笔记 更新ing~
Effective C++ 中文版(第三版)持续更新ing 让自己习惯C++ 条款1 视c++为一个联邦 条款2 尽量以const,enum,inline替换#define 条款3 尽可能使用cons ...
- [大话设计模式C++版] 第14章 老板回来,我不知道 —— 观察者模式
源码可以在这里找到 大话设计模式C++版 双向耦合的代码 //Secretary.h 秘书类 #include <QList>using namespace std; class Stoc ...
- 《大话设计模式(C#实现)》(Yanlz+VR云游戏+Unity+SteamVR+云技术+5G+AI+设计模式+GoF+UML+单例模式+观察者模式+抽象工厂+代理模式+框架编程+立钻哥哥++OK+)
<大话设计模式(C#实现)> 版本 作者 参与者 完成日期 备注 YanlzFramework_GoF_V01_1.0 严立钻 2020.02.10 ##<大话设计模式(C#实现)& ...
- 大话设计模式——总结
简单工厂模式:每一次扩展都要更改工厂类,对修改开放,不符合开闭原则. 对象比流程更加稳定,也更加封闭. 一.创建型模式 创建型模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的 ...
- java思想 设计模式 《大话设计模式》知识总结
本文大部分内容来自<大话设计模式>. java很适合多人协作,但代码量很大. 与其抱怨需求总是变化,不如改变开发过程,从而更有效地应对变化.面向对象就是为了解决变化的问题. 文章目录 1. ...
最新文章
- linux基础-延时命令:sleep
- RPC框架原理及从零实现系列博客(二):11个类实现简单RPC框架
- k8s组件批量启动、查看状态
- python获取qq好友ip_qqzeng-ip.dat IP库读取python版
- Springboot注册Filter
- 查看某个端口是否链接超时
- jQuery 事件 - bind() 方法
- 数据脱敏:数仓安全隐私保护见真招儿
- 『C#基础』XML文件的读与写
- 第一冲刺阶段(第四天)
- Java JavaEE JavaSE JavaME JavaWEB 之间的区别与联系
- linux 安装Gauss09 GaussView
- 控制系统--系统结构图
- 科研必备之图像局部区域放大——画中画形式
- 黑客利用2012伦敦奥运诈骗个人资料
- 啤酒和尿布:一文看懂关联规则
- 华为Mate40 Pro/Pro+正式发布 价格曝光
- es高级客户端聚合查询api快速入门
- IDM2022最新版极速下载工具
- linux-进程间通信
热门文章
- matlab 三围温度,matlab 两个二维温度场平面如何通过插值立体重建三维温度场
- linux spec文档解析
- 诺基亚升级Android10,诺基亚发布第五次Android 10更新 ,诺基亚7+可升级
- boost heap - d_ary_heap 的自定义compare函数用法
- 快手广告投放方式有哪些?快手广告怎么计费的?
- 21闭关修炼 解析分册
- opencv3/C++ 将图片转换为视频
- arcengine 面积单位转换
- python tkinter label标签_Python Tkinter详解 (二)Label标签的使用
- ICC Floorplan遇到的坑以及解决方法