首页 > 代码库 > Java 下各种设计模式小结
Java 下各种设计模式小结
策略模式——定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
策略模式是说,针对一种计算,定义一系列的算法,由用户决定具体使用哪一个算法完成计算。
比如,提供一个计算银行存款利率的接口,对于不同的存款方式(活期、一年定期、两年定期),提供不同的算法实现类,由用户决定使用哪种存款方式来计算利率。如果银行计算利率的算法发生了变化(如又增加了三年定期、五年定期的算法),对于用户的使用不产生任何影响,因为用户使用的是统一的计算接口,也符合了针对接口编程,不针对实现编程的设计原则。
定义一个计算存款利率的接口:
- public interface IRateCalculator {
- public double calculate(double amount);
- }
计算活期存款利率的实现类:
- public class CurrentRateCalcalator implements IRateCalculator {
- private double rate = 0.035;
- /**
- * 计算活期存款利率
- */
- @Override
- public double calculate(double amount) {
- return amount * rate;
- }
- }
计算一年定期存款利率的实现类:
- public class OneYearRateCalculator implements IRateCalculator {
- private double rate = 0.0325;
- /**
- * 计算一年定期存款利率
- */
- @Override
- public double calculate(double amount) {
- return amount * rate;
- }
- }
计算两年定期存款利率的实现类:
- public class TwoYearRateCalculator implements IRateCalculator {
- private double rate = 0.0375;
- /**
- * 计算两年定期存款利率
- */
- @Override
- public double calculate(double amount) {
- return amount * rate;
- }
- }
测试类:
- public class Test {
- /**
- * 策略模式——定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
- *
- * 针对一种计算,定义一系列的算法,由用户决定具体使用哪一个算法完成计算。
- * @param args
- */
- public static void main(String[] args) {
- //假设有100块钱的本金
- double amount = 100;
- double rate;
- IRateCalculator rateCalculator;
- //计算活期存款利率
- rateCalculator = new CurrentRateCalcalator();
- rate = rateCalculator.calculate(amount);
- System.out.println("活期存款利率为:" + rate);
- //计算一年定期存款利率
- rateCalculator = new OneYearRateCalculator();
- rate = rateCalculator.calculate(amount);
- System.out.println("一年定期存款利率为:" + rate);
- //计算两年定期存款利率
- rateCalculator = new TwoYearRateCalculator();
- rate = rateCalculator.calculate(amount);
- System.out.println("两年定期存款利率为:" + rate);
- }
- }
命令模式(Command Pattern)——将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持科撤销的操作。
命令模式适用于“请求-响应”模式的功能,将用户的请求封装成对象(命令),用户需要执行什么样的操作,就调用什么样的命令,而无需知道命令的执行逻辑是什么。
命令模式主要包含以下几个概念:
1、Command:所有命令的抽象类,一般需要对外公开一个执行命令的方法execute,如有需要还需提供一个命令的撤销方法undo。
2、ConcreteCommand:命令的实现类。
3、Invoker:调用者,负责命令的调度。
4、Reveiver:接收者,负责命令的接收和执行。
5、Client:客户端,命令的发起者。
比如,一个有存取款功能的ATM机,它可以向某个银行的卡里存款,也可以从任何支持银联接口的银行卡里取款。不同的银行系统对存款、取款功能的实现均有不同,我们不关心银行怎么实现,只要点击ATM上的按钮就行了。这个例子中,银行的存款和取款分别为两个具体的命令实现类(ConcreteCommand);ATM机充当调用者(Invoker),负责调用银行存款或取款的命令;银行的系统为接收者(Reveiver),处理存款和取款的业务逻辑;使用ATM机的人就是客户端。
首先定义一个命令的抽象类,只有两个方法,执行和撤销:
- public interface Command {
- public void execute();
- public void undo();
- }
模拟两个银行的系统,建行(CCB)和招行(CMB),简单一点,只有存款和取款的功能。为了模拟银行系统实现的差异,将存款、取款方法取成不同的名字。
建行:
- public class Ccb {
- public void cunqian(long amount) {
- System.out.println("向建设银行存入金额:" + amount);
- }
- public void quqian(long amount) {
- System.out.println("从建设银行取出金额:" + amount);
- }
- }
招行:
- public class Cmb{
- public void saveMoney(long amount) {
- System.out.println("向招商银行存入金额:" + amount);
- }
- public void getMoney(long amount) {
- System.out.println("从招商银行取出金额:" + amount);
- }
- }
将银行的存款、取款动作封装成命令对象。为了避免太复杂,存款命令的撤销就当再取出来,取款命令的撤销就再存回去。
建行的存款命令:
- public class CcbDepositCommand implements Command {
- private Ccb ccb = new Ccb();
- @Override
- public void execute() {
- ccb.cunqian(100);
- }
- @Override
- public void undo() {
- ccb.quqian(100);
- }
- }
建行的取款命令:
- public class CcbWithdrawCommand implements Command {
- private Ccb ccb = new Ccb();
- @Override
- public void execute() {
- ccb.quqian(100);
- }
- @Override
- public void undo() {
- ccb.cunqian(100);
- }
- }
招行的存款命令:
- public class CmbDepositCommand implements Command {
- private Cmb cmb = new Cmb();
- @Override
- public void execute() {
- cmb.saveMoney(100);
- }
- @Override
- public void undo() {
- cmb.getMoney(100);
- }
- }
招行的取款命令:
- public class CmbWithdrawCommand implements Command {
- private Cmb cmb = new Cmb();
- @Override
- public void execute() {
- cmb.getMoney(100);
- }
- @Override
- public void undo() {
- cmb.saveMoney(100);
- }
- }
ATM机刚出厂时可能还没设置任何命令,所以为每个按钮预置一个什么都不做的命令:
- public class NoCommand implements Command {
- @Override
- public void execute() { }
- @Override
- public void undo() { }
- }
现在来实现一个刚出厂的ATM机,它可能完成任何银行的存取款命令,这些命令由负责购买ATM机的银行日后自行规划:
- public class Atm {
- private Command[] command;
- public Atm(){
- this.command = new Command[]{new NoCommand()};
- }
- //设置一组要执行的命令
- public void setCommand(Command command[]) {
- this.command = command;
- }
- //执行命令的方法
- public void action(int i) {
- this.command[i].execute();
- }
- //撤销命令的方法
- public void cancel(int i) {
- this.command[i].undo();
- }
- }
现在假设要实现一个建行的ATM机,只有三个功能:1是向建行存款;2是从建行取款;3从招行取款。只要为ATM设置上相应的命令就可以了。测试类:
- public class Test {
- /**
- * 命令模式——将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。
- * 命令模式也支持科撤销的操作。
- *
- * @param args
- */
- public static void main(String[] args) {
- //调用者来执行命令
- Atm atm = new Atm();
- Command[] commands = new Command[3];
- commands[0] = new CcbDepositCommand();
- commands[1] = new CcbWithdrawCommand();
- commands[2] = new CmbWithdrawCommand();
- atm.setCommand(commands);
- atm.action(0);
- atm.cancel(0);
- atm.action(1);
- atm.cancel(1);
- atm.action(2);
- atm.cancel(2);
- }
- }
当然,如果我们不适用ATM机,直接到银行的窗口,营业员也可以直接调用系统相应的命令:
- //直接执行具体命令
- Command command = new CcbDepositCommand();
- command.execute();
- command.undo();
命令模式的扩展性、封装性很好,可以很好的将用户请求与请求的实现解耦,对需求的变化也更容易扩展。但一个很简单的请求都需要封装为一个命令,也会导致类的膨胀,因此开发时需根据实际需要判断是否使用命令模式。
模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模版方法模式适用于一组固定流程的算法,在抽象类中定义一组算法,由子类去实现,抽象类提供一个公开方法,确定调用这组算法的步骤。
比如,我们去营业厅办理一张手机卡,不论是移动、联通还是电信,流程都是先办卡、再选号,而办卡和选号的动作每个运营商自己去实现。
我们定义一个运营商的抽象类,有两个抽象的方法办卡和选号,还有一个方法就是提供服务,调用办卡和选号:
- public abstract class ServiceOperator {
- protected abstract void requestCard();
- protected abstract void selectNumber();
- final public void service(){
- this.requestCard();//先办卡
- this.selectNumber();//再选号
- }
- }
分别定义两个实现类,移动和联通:
- public class ChinaMobile extends ServiceOperator {
- @Override
- protected void requestCard() {
- System.out.println("办一张中国移动电话卡");
- }
- @Override
- protected void selectNumber() {
- System.out.println("选一个中国移动电话号");
- }
- }
- public class ChinaUnicom extends ServiceOperator {
- @Override
- protected void requestCard() {
- System.out.println("办一张中国联通电话卡");
- }
- @Override
- protected void selectNumber() {
- System.out.println("选一个中国联通电话号");
- }
- }
这样,不论哪个运营商要修改办卡或是选号的操作时,对我们的整个流程不会产生任何影响了。对于办卡和选号这两个动作,称之为基本方法,由具体的实现类去完成;提供服务的方法称之为模版方法,定义了基本方法的调用流程。
测试类:
- public class Test {
- /**
- * 模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。
- * 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- *
- * @param args
- */
- public static void main(String[] args) {
- ServiceOperator serviceOperator = new ChinaUnicom();
- serviceOperator.service();
- }
- }
模版方法模式还可以进一步进行扩展。对于某些基本方法的调用与否,可能需要一些条件,这时,我们可以在模版抽象类中定义一个控制这些条件的方法,称之为钩子方法(Hood Method),由子类去设定这个条件,以使不同的实现类能够做出不同的处理。
比如,手机卡丢了,要去营业厅补一张,移动说,丢了没办法,重办卡、重选号吧,联通为了抢客户,就说,办张卡,号还用原来那个,不用选了。为了实现这个逻辑,我们在运营商的抽象类中增加一个判断是否为新用户的方法,如果是新用户,就不调用选号的方法了:
- public abstract class ServiceOperator {
- protected abstract void requestCard();//基本方法
- protected abstract void selectNumber();//基本方法
- //模版方法
- final public void service(){
- this.requestCard();//先办卡
- if(this.isNewCustomer()){
- this.selectNumber();//再选号
- }
- }
- //钩子方法(Hood Method)
- protected boolean isNewCustomer(){
- return true;
- }
- }
移动不管他是新用户还是老用户,全当新用户处理:
- public class ChinaMobile extends ServiceOperator {
- @Override
- protected void requestCard() {
- System.out.println("办一张中国移动电话卡");
- }
- @Override
- protected void selectNumber() {
- System.out.println("选一个中国移动电话号");
- }
- @Override
- protected boolean isNewCustomer() {
- return true;
- }
- }
联通让用户自己决定,老用户就可以不选号,光办卡:
- public class ChinaUnicom extends ServiceOperator {
- private boolean isNewCustomer = true;
- @Override
- protected void requestCard() {
- System.out.println("办一张中国联通电话卡");
- }
- @Override
- protected void selectNumber() {
- System.out.println("选一个中国联通电话号");
- }
- @Override
- protected boolean isNewCustomer() {
- return this.isNewCustomer;
- }
- public void setNewCustomer(boolean isNewCustomer) {
- this.isNewCustomer = isNewCustomer;
- }
- }
测试类:
- public class Test {
- /**
- * 模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。
- * 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- *
- * @param args
- */
- public static void main(String[] args) {
- ChinaUnicom chinaUnicom = new ChinaUnicom();
- chinaUnicom.setNewCustomer(false);
- chinaUnicom.service();
- }
- }
单例模式——确保一个类只有一个实例,并提供一个全局访问点。
单例模式一般分为懒汉式和恶汉式,懒汉式是说当第一次获取类时才进行类的实例化,饿汉式是说当类被加载时直接实例化。定义单例模式的一般步骤是:
* 定义一个私有的构造函数,以保证这个类不能被外部程序实例化;
* 定义一个类的实例变量,以保存这个类的唯一实例;
* 定义一个获取类唯一实例的静态方法,使外部程序可以获取这个类的唯一实例。
懒汉式:
- public class Singleton {
- private static Singleton uniqueInstance;
- private Singleton() {}
- public static synchronized Singleton getInstance() {
- if (uniqueInstance == null) {
- uniqueInstance = new Singleton();
- }
- return uniqueInstance;
- }
- }
使用synchronized关键字保证获取实例时,如果实例为null,只有一个线程去创建该实例,但这样做会导致效率低下,下面有更好的解决办法
线程安全的懒汉式:
- public class Singleton {
- private volatile static Singleton uniqueInstance;
- private Singleton() {}
- public static Singleton getInstance() {
- if (uniqueInstance == null) {
- synchronized (Singleton.class) {
- if (uniqueInstance == null) {
- uniqueInstance = new Singleton();
- }
- }
- }
- return uniqueInstance;
- }
- }
用“双重检查加锁”,在getInstance中减少使用同步。volatile关键字确保,当uniqueInstance变量被初始化成 Singleton实例时,多个线程正确的处理uniqueInstance变量。注意,1.4及更早的Java中,许多JVM对于volatile关键 字的实现会导致双重加锁失效。
饿汉式:
- public class Singleton {
- private static Singleton uniqueInstance = new Singleton();
- private Singleton() {}
- public static Singleton getInstance() {
- return uniqueInstance;
- }
- }
饿汉式在类被加载时直接实例化,因此不存在获取实例时的线程安全问题。
工厂模式(Factory Pattern)
比 如,你要办一张联通的电话卡,联通有A计划套餐、B计划套餐,不管什么套餐,都能打电话,你只要到营业厅买一张卡就行了。其中,电话卡是一个接口 (SimCard),它有一个方法就是用来打电话(service),A计划和B计划的卡都是其实现类(SimCardPlanA、 SimCardPlanB),营业厅就是一个工厂,用来生产电话卡(ChinaUnicomFactory),你只要告诉它你要什么套餐就行了。
电话卡接口(SimCard):
- public interface SimCard {
- public void service();
- }
A套餐的卡(SimCardPlanA):
- public class SimCardPlanA implements SimCard {
- @Override
- public void service() {
- System.out.println("联通A计划电话卡");
- }
- }
B套餐的卡(SimCardPlanB):
- public class SimCardPlanB implements SimCard {
- @Override
- public void service() {
- System.out.println("联通B计划电话卡");
- }
- }
生产电话卡的工厂,也就是营业厅(ChinaUnicomFactory):
- public class ChinaUnicomFactory {
- public static SimCard getSimCard(String kind) {
- if("PlanA".equals(kind)){
- return new SimCardPlanA();
- }else if("PlanB".equals(kind)){
- return new SimCardPlanB();
- }else{
- System.out.println("中国联通尚未推出该套餐");
- return null;
- }
- }
- }
测试类:
- public class Test {
- /**
- * 又称静态工厂方法模式(Static Factory Method Pattern),很简单,
- * 就是通过一个工厂类来负责对象的创建。
- *
- * @param args
- */
- public static void main(String[] args) {
- //获取一个联通A计划的电话卡
- SimCard simCard1 = ChinaUnicomFactory.getSimCard("PlanA");
- simCard1.service();
- //获取一个联通B计划的电话卡
- SimCard simCard2 = ChinaUnicomFactory.getSimCard("PlanB");
- simCard2.service();
- }
- }
继 续上面的例子,能生产电话卡的不光是联通啊,移动、电信也行啊,只要是运营商都卖手机卡啊。于是,我们将上面的工厂改造一下,提供一个运营商工厂的接口 (ServiceOperatorFactory),联通工厂(ChinaUnicomFactory)、移动工厂 (ChinaMobileFactory)都实现了运营商接口的实现类,再增加两种卡的类型,移动的全球通卡(SimCardQQT)和神州行卡 (SimCardSZX)。这样,当用户想办联通卡时就用联通的工厂,想用移动卡时就用移动的工厂,没准哪天都不想用了,想换个电信的,没问题,不用修改 写好的代码,再加一个电信的工厂就行了,扩展性得到提高了吧。
运营商接口(ServiceOperatorFactory):
- public interface ServiceOperatorFactory {
- public SimCard getSimCard(String kind);
- }
联通工厂(ChinaUnicomFactory):
- public class ChinaUnicomFactory implements ServiceOperatorFactory {
- @Override
- public SimCard getSimCard(String kind) {
- if("PlanA".equals(kind)){
- return new SimCardPlanA();
- }else if("PlanB".equals(kind)){
- return new SimCardPlanB();
- }else{
- System.out.println("中国联通尚未推出该套餐");
- return null;
- }
- }
- }
移动工厂(ChinaMobileFactory):
- public class ChinaMobileFactory implements ServiceOperatorFactory {
- @Override
- public SimCard getSimCard(String kind) {
- if("QQT".equals(kind)){
- return new SimCardQQT();
- }else if("SZX".equals(kind)){
- return new SimCardSZX();
- }else{
- System.out.println("中国移动尚未推出该套餐");
- return null;
- }
- }
- }
联通的A计划、B计划卡和上面一样,不列举了。
移动的全球通卡(SimCardQQT):
- public class SimCardQQT implements SimCard {
- @Override
- public void service() {
- System.out.println("移动全球通电话卡");
- }
- }
移动的神州行卡(SimCardSZX):
- public class SimCardSZX implements SimCard {
- @Override
- public void service() {
- System.out.println("移动神州行电话卡");
- }
- }
测试类:
- public class Test {
- /**
- * 工厂方法模式(Factory Method Pattern)——定义了一个创建对象的接口,
- * 但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
- *
- * @param args
- */
- public static void main(String[] args) {
- ServiceOperatorFactory factory;
- //获取一个联通A计划的电话卡
- factory = new ChinaUnicomFactory();
- SimCard simCard1 = factory.getSimCard("PlanA");
- simCard1.service();
- //获取一个移动神州行的电话卡
- factory = new ChinaMobileFactory();
- SimCard simCard2 = factory.getSimCard("SZX");
- simCard2.service();
- }
- }
抽象工厂模式(Abstract Factory Pattern)——提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
还 是继续上面的例子,现在运营商可不光是卖卡了,还卖手机。你想买个IPhone5,还得分清是买联通版还是移动版。去联通营业厅,肯定不能买到移动版的 IPhone5啊。于是,我们改造一下上面的例子,增加一个电话的接口(Phone),有一个方法打电话(call),定义两个实现类,联通版 IPhone(IPhoneUnicom)和移动版IPhone(IPhoneMobile),在运营商接口中增加一个获取电话的方法 (getPhone,假设营业厅只卖IPhone)。当使用不同的运营商工厂时,得到的电话卡和电话都是指定运营商的,你不需要指定说我要个什么版本的电 话,因为联通不卖移动版的电话。
电话接口(Phone):
- public interface Phone {
- public void call();
- }
联通版IPhone(IPhoneUnicom):
- public class IPhoneUnicom implements Phone {
- @Override
- public void call() {
- System.out.println("正在使用IPhone联通版");
- }
- }
移动版IPhone(IPhoneMobile):
- public class IPhoneMobile implements Phone {
- @Override
- public void call() {
- System.out.println("正在使用IPhone移动版");
- }
- }
运营商接口(ServiceOperatorFactory):
- public interface ServiceOperatorFactory {
- public SimCard getSimCard(String kind);
- public Phone getPhone();
- }
联通工厂(ChinaUnicomFactory):
- public class ChinaUnicomFactory implements ServiceOperatorFactory {
- @Override
- public SimCard getSimCard(String kind) {
- if("PlanA".equals(kind)){
- return new SimCardPlanA();
- }else if("PlanB".equals(kind)){
- return new SimCardPlanB();
- }else{
- System.out.println("中国联通尚未推出该套餐");
- return null;
- }
- }
- @Override
- public Phone getPhone() {
- return new IPhoneUnicom();
- }
- }
移动工厂(ChinaMobileFactory):
- public class ChinaMobileFactory implements ServiceOperatorFactory {
- @Override
- public SimCard getSimCard(String kind) {
- if("QQT".equals(kind)){
- return new SimCardQQT();
- }else if("SZX".equals(kind)){
- return new SimCardSZX();
- }else{
- System.out.println("中国移动尚未推出该套餐");
- return null;
- }
- }
- @Override
- public Phone getPhone() {
- return new IPhoneMobile();
- }
- }
电话卡接口和实现类同上,不再列举。
测试类:
- public class Test {
- /**
- * 抽象工厂模式(Abstract Factory Pattern)——提供一个接口,
- * 用于创建相关或依赖对象的家族,而不需要明确指定具体类。
- *
- * @param args
- */
- public static void main(String[] args) {
- ServiceOperatorFactory factory;
- //获取一个联通A计划的电话卡
- factory = new ChinaUnicomFactory();
- SimCard simCard1 = factory.getSimCard("PlanA");
- Phone phone1 = factory.getPhone();
- simCard1.service();
- phone1.call();
- //获取一个移动神州行的电话卡
- factory = new ChinaMobileFactory();
- SimCard simCard2 = factory.getSimCard("SZX");
- Phone phone2 = factory.getPhone();
- simCard2.service();
- phone2.call();
- }
- }
区别:
简单工厂模式多用于需求明确、功能简单的开发,通过一个工厂类就可完成所有产品的生成工作。
工厂方法模式侧重点是,开发时不明确要生成哪种类型的实例,不同的工厂类生成不同类型的产品,运行时通过指定不同的工厂类来生成相应的产品类型。如有产品类型A,工厂1用于生成产品A1,工厂2用于生成产品A2,运行时通过指定不同的工厂来生成不同的产品。
抽 象工厂模式更注重于生产一个产品家族(即多种类型的产品),而这些产品类型又可横向划分为多个系列,那么,抽象工厂中定义生成所有产品类型的方法,每个工 厂的实现类用于生产一个系列所有类型的产品。如有A、B、C等产品类型,而这些类型中又可分为多个系列1、2,这样就有了产品A1、A2、B1、B2、 C1、C2,这时,工厂1用于生产A1、B1、C1,工厂2用于生产A2、B2、C2,运行时通过指定不同的工厂来生成不同系列的一组产品。
装饰者模式——动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
装饰者模式就是给一个对象动态的添加新的功能,装饰者和被装饰者实现同一个接口,装饰者持有被装饰者的实例。JAVA中IO就大量使用了装饰者模式,如:
- InputStream in = new BufferedInputStream(new FileInputStream("test.txt"));
其 中FileInputStream、BufferedInputStream都实现了InputStream,BufferedInputStream就 是一个装饰者,增加利用缓冲输入来改进性能,以及FileInputStream所没有的readLine()方法来增强接口。
假如我们有一个系统监控的接口,它的功能很简单,就是当系统发生异常时进行处理动作。然后我们实现了一个能够日志记录的实现类,当系统发生异常只要把日志 记录好就可以了。可是后来我们又想记录完日志后需要给维护人员发邮件,根据开闭原则,我们不能去修改记录日志的类啊,所以这时就需要用到装饰模式了,定义 一个能发送邮件的监控接口实现类,它其中保持了一个监控接口的实例(那个能记日志的实现类),这时,我们调用本实现类时,就即能记录日志,又能发邮件了。 再后来,老大又想加一个系统发生严重异常时能电话通知的功能,没关系,再写一个能电话通知的实现类,什么都不用改,OK了!
系统监控的接口:
- public interface Monitor {
- //系统发生异常时的处理方法
- public void handle();
- }
- public class LoggerMonitor implements Monitor {
- @Override
- public void handle() {
- System.out.println("日志已记录;");
- }
- }
- public class EmailMonitor implements Monitor {
- private Monitor monitor;
- public EmailMonitor(Monitor monitor){
- this.monitor = monitor;
- }
- @Override
- public void handle() {
- monitor.handle();
- System.out.println("邮件已发送;");
- }
- }
- public class PhoneMonitor implements Monitor {
- private Monitor monitor;
- public PhoneMonitor(Monitor monitor){
- this.monitor = monitor;
- }
- @Override
- public void handle() {
- this.monitor.handle();
- System.out.println("已电话通知");
- }
- }
测试类:
- public class Test {
- /**
- * 装饰者模式——动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
- *
- * 装饰者模式就是给一个对象动态的添加新的功能,装饰者和被装饰者实现同一个接口,装饰者持有被装饰者的实例。
- * @param args
- */
- public static void main(String[] args) {
- //既想记录日志又想邮件通知
- Monitor monitor = new EmailMonitor(new LoggerMonitor());
- monitor.handle();
- System.out.println("------------------------------");
- //既想记录日志又想电话通知
- monitor = new PhoneMonitor(new LoggerMonitor());
- monitor.handle();
- System.out.println("------------------------------");
- //既想记录日志,又发邮件,又打电话
- monitor = new PhoneMonitor(new EmailMonitor(new LoggerMonitor()));
- monitor.handle();
- }
- }
观察者模式——定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式是说,当一个对象的状态发生改变的时候,关心这个对象的所有对象都会接到通知,并作出相应的反应。比如,公司的OA系统提供了消息订阅功能,当有新的消息产生时,所有订阅了该消息的员工都会接到通知,这就是观察者模式。
观察者模式的核心是一个主题接口,一个观察者接口,任何实现了主题接口的实现类都可以被观察者订阅(如会议通知、项目动态都可以被员工订阅),任何实现了观察者接口的实现类都可以订阅、或取消订阅一个主题(如中层员工订阅了会议通知、基础员工订阅了项目动态)。
主题接口:
- public interface Subject {
- //注册一个观察者
- public void registerObserver(Observer o);
- //移除一个观察者
- public void removeObserver(Observer o);
- //通知所有观察者
- public void notifyObservers();
- }
观察者接口:
- public interface Observer {
- //观察者作出反应
- public void update();
- }
一个消息主题(主题接口的实现类),主题记录了所有的观察者列表和主题的状态,当主题的状态发生改变时,可以通知所有订阅者做出相应的反应:
- import java.util.ArrayList;
- public class MsgSubject implements Subject {
- private ArrayList<Observer> observers;//观察者列表
- private String msg;//消息(状态)
- public MsgSubject(){
- this.observers = new ArrayList<Observer>();
- }
- @Override
- public void registerObserver(Observer o) {
- observers.add(o);
- }
- @Override
- public void removeObserver(Observer o) {
- int i = observers.indexOf(o);
- if (i >= 0) {
- observers.remove(i);
- }
- }
- @Override
- public void notifyObservers() {
- for (Observer o : this.observers) {
- o.update();
- }
- }
- //获取主题状态的方法
- public String getMsg() {
- return msg;
- }
- //设置主题状态的方法
- public void setMsg(String msg) {
- this.msg = msg;
- }
- }
观察者1:
- public class Observer1 implements Observer {
- private MsgSubject msgSubject;//记录主题的引用,当主题状态发生变化时,可向主题获取状态信息
- public Observer1(MsgSubject msgSubject){
- this.msgSubject = msgSubject;
- msgSubject.registerObserver(this);//向主题注册此观察者
- }
- @Override
- public void update() {
- System.out.println("Observer1收到通知:" + this.msgSubject.getMsg());
- }
- }
观察者2:
- public class Observer2 implements Observer {
- private MsgSubject msgSubject;//记录主题的引用,当主题状态发生变化时,可向主题获取状态信息
- public Observer2(MsgSubject msgSubject){
- this.msgSubject = msgSubject;
- msgSubject.registerObserver(this);//向主题注册此观察者
- }
- @Override
- public void update() {
- System.out.println("Observer2收到通知:" + this.msgSubject.getMsg());
- }
- }
测试类:
- public class Test {
- /**
- * 观察者模式——定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
- *
- * 主题接口的实现类记录了所有的观察者列表和主题的状态,可以向一个主题注册或取消注册一个观察者,
- * 当主题的状态发生改变时,可以通知所有订阅者做出相应的反应
- * @param args
- */
- public static void main(String[] args) {
- MsgSubject msgSubject = new MsgSubject();
- Observer1 o1 = new Observer1(msgSubject);
- Observer2 o2 = new Observer2(msgSubject);
- msgSubject.setMsg("第一条消息");//改变主题状态
- msgSubject.notifyObservers();//通知所有观察者作出反应
- msgSubject.setMsg("第二条消息");//改变主题状态
- msgSubject.notifyObservers();//通知所有观察者作出反应
- msgSubject.removeObserver(o1);//观察者1取消订阅
- msgSubject.setMsg("第三条消息");//改变主题状态
- msgSubject.notifyObservers();//通知所有观察者作出反应
- }
- }
Java 下各种设计模式小结