做开发的你,怎么能没读过《Head First设计模式》(二)

举报
山河已无恙 发表于 2021/12/04 09:23:07 2021/12/04
【摘要】 我只是怕某天死了,我的生命却一无所有。----《奇幻之旅》

写在前面:


  • 接上一篇
  • 博客主要是以书里内容为主,关于博客里的UML图,小伙伴有需要的可以联系我,生活加油 😦

  • 笔记内容:这本书的设计模式不全,所以结合菜鸟教程上的笔记,还有那本著名的《设计模式_可复用面向对象软件的基础》的部分,《设计模式_可复用面向对象软件的基础》这本以后有时间刷,感觉非常不错,就是有些重,有些还是读不懂,水平有限,不适合快速学习,如果时间多,强烈建议小伙伴真的要刷一下,超级棒,需要资源可以联系我 😃

我只是怕某天死了,我的生命却一无所有。----《奇幻之旅》

观察者(Observer)模式-对象行为型

观察者模式观察者模式定义了对象之间一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新

是JDK中使用最多的模式之一,·出版者(Subject)+订阅者(Observer)=观察者模式
观察者模式的通知机制与其他设计模式委托机制是不同的,前者是“一对多”的对象之间的通信,后者是“一对一”的对象之间的通信在这里插入图片描述

情境:一个气象观测站,需要实时更新观测的信息,分别发布到三个公告栏上,一旦气象测量更新,调用更新的方法,不同的公告栏调用相同的更新方法,类似于报纸和杂志的订阅.这种发布-订阅的模式为观察者模式,对于观察者模式来讲,在需要观察的时候要注册,不需要时取消订阅,数据更新时,通知现有观察者.
在这里插入图片描述

观察者模式定义了一系列对象之间的一对多关系。当一个对象改变状态,其他依赖者都会收到通知。
在这里插入图片描述

·定义观察者模式
在这里插入图片描述

松耦合的威力:观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
设计原则:为了交互对象之间的松耦合设计而努力。松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。

回到气象观测站

WeatherData类具有getter方法,可以取得三个测量值:温度、湿度与气压
当新的测量数据备妥时,measurementsChanged()方法就会被调用
实现三个使用天气数据的布告板:“目前状况”布告、“气象统计”布告、“天气预报”布告
在这里插入图片描述

`问 `:为什么要保存对Subject的引用呢?构造完后似乎用不着了呀?
`答`:的确如此,但是以后我们可能想要取消注册,如果已经有了对Subject的引用会比较方便。
代码实现
Subject.java
package com.liruilong.design_pattern.Observer;

/*
 * @return
 * @Description 主题
 * @author Liruilong
 * @date  2021/5/9  22:24
 **/
public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}
Observer.java
package com.liruilong.design_pattern.Observer;

/*
 * @Description 观察者
 * @author Liruilong
 * @date  2021/5/9  22:24
 **/
public interface Observer {

    /*
     * @return
     * @Description TODO  当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者。
     * @author Liruilong
     * @date  2021/5/9  22:23
     **/
    public void update(float temp, float humidity, float pressure);
}
DisplayElement.java
package com.liruilong.design_pattern.Observer;

/*
 * @return
 * @Description DisplayElement接口只包含了一个方法,也就是display()。当布告板需要显示时,调用此方法。
 * @author Liruilong
 * @date  2021/5/9  22:26
 **/
public interface DisplayElement {
    public void display();
}

WeatherData.java
package com.liruilong.design_pattern.Observer;

import java.util.ArrayList;
/*
 * @return 
 * @Description  Subject主题实现类
 * @author Liruilong
 * @date  2021/5/10  0:23
 **/
public class WeatherData implements  Subject{

    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){
        observers = new ArrayList();
    }
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        observers.forEach( (o) ->Observer.class.cast(o).update(temperature, humidity, pressure));
    }

    //通知观察者
    public void measurementsChanged() {
        notifyObservers();
    }
    //测试布告板能否正常推送,模拟数据改变通知观察
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
CurrentConditionsDisplay.java
package com.liruilong.design_pattern.Observer;

/*
 * @return
 * @Description 公告板
 * @author Liruilong
 * @date  2021/5/9  22:53
 **/
public class CurrentConditionsDisplay  implements Observer, DisplayElement{
    //温度
    private float temperature;
    //湿度
    private float humidity;
    private Subject weatherData;

    // 构造器需要 weatherData对象(也就是主题)作为注册之用。
    public CurrentConditionsDisplay(Subject weatherData) {
        /*
        *  `问 `:为什么要保存对Subject的引用{@code weatherData}呢?构造完后似乎用不着了呀?
        *  `答`:的确如此,但是以后我们可能想要`取消注册`,如果已经有了对Subject的引用会比较方便。
        * */
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public CurrentConditionsDisplay() {

    }

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature
                + "F degrees and " + humidity + "% humidity");
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
}
WeatherStation.java 测试类,通过setMeasurements改变值触发订阅推送。
package com.liruilong.design_pattern.Observer;

public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        //CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        //weatherData.registerObserver(currentDisplay);
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        // StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        // ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        //weatherData.removeObserver(currentDisplay);
        weatherData.setMeasurements(78, 90, 29.2f);


    }

}

使用JDK内置的观察者模式

Java API有内置的观察者模式java.util包(package)内包含最基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。你甚至可以使用推(push)拉(pull)的方式传送数据

Java内置的观察者模式如何运作,嘻嘻,源码很容易懂,直接上源码,多了一个更新状态标识
Observer .java
package java.util;

/**
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    void update(Observable o, Object arg);
}
Observable.java
package java.util;

/**
 *......
 * @author  Chris Warth
 * @see     java.util.Observable#notifyObservers()
 * @see     java.util.Observable#notifyObservers(java.lang.Object)
 * @see     java.util.Observer
 * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
 * @since   JDK1.0
 */
public class Observable {
    // 标记状态是否被改变
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return obs.size();
    }
}
利用内置的支持重做气象站
CurrentConditionsDisplay.java
package com.liruilong.design_pattern.Observerjdk.Observer;

import java.util.Observable;
import java.util.Observer;

/*
 * @return
 * @Description  实现了Observer的公告板
 * @author Liruilong
 * @date  2021/5/10  1:42
 **/
public class CurrentConditionsDisplay  implements Observer,DisplayElement{
    //温度
    private float temperature;
    //湿度
    private float humidity;
   // 主题
    Observable observable;

    // 构造器需要 weatherData对象(也就是主题)作为注册之用。
    public CurrentConditionsDisplay(Observable observable) {
            this.observable = observable;
            observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData){
            WeatherData weatherData = (WeatherData)o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }
    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature
                + "F degrees and " + humidity + "% humidity");
    }
}

WeatherData.java
package com.liruilong.design_pattern.Observerjdk.Observer;


import java.util.Observable;

/*
 * @return
 * @Description  继承了Observable的主题
 * @author Liruilong
 * @date  2021/5/10  1:41
 **/
public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;
    //通知观察者
    public void measurementsChanged() {
        //更新标识
        setChanged();
        //我们没有调用notifyObservers()传送数据对象,这表示我们采用的做法是“拉”。
        notifyObservers();
    }
    //测试布告板能否正常推送,模拟通知观察
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

oo原则: 为交互对象之间的松耦合设计而努力
用java8写一个观察者
申明一个观察者接口,用来泛化观察行为
/**
      * @Author Liruilong
      * @Description  观察者模式
      * @Date 8:47 2019/9/5
      **/
     // 声明一个观察者接口,将不同的观察者聚合在一起
     @FunctionalInterface
     interface  Observer{
          void notify(String tweet);
     }
申明几个观察者
     // 申明不同的观察者,定义不同的行为
     class NYTimes implements Observer{
 
         @Override
         public void notify(String tweet) {
            if (tweet != null && tweet.contains("money")){
                System.out.println("Breaking news in NY !" + tweet);
            }
         }
     }
     class LeMonde implements  Observer{
 
         @Override
         public void notify(String tweet) {
             if (tweet != null && tweet.contains("queen")){
                 System.out.println(""+tweet);
             }
         }
     }
     class  Guardian implements  Observer{
 
         @Override
         public void notify(String tweet) {
             if (tweet != null && tweet.contains("wine")){
                 System.out.println("Today cheese ,wine and news!"+tweet);
             }
         }
     }
申明主题
     interface Subject{
         // 注册一个新的观察者
         void registerObserver(Observer o);
         // 通知观察者行为变化
         void notifyObservers(String twee);
     }
发布-订阅
     /**
      * @Author Liruilong
      * @Description  内部维护一个观察者列表.
      * @Date 9:44 2019/9/5
      * @Param
      * @return
      **/
 
     class Feed implements Subject{
 
         private final List<Observer> observers = new ArrayList<>();
 
         @Override
         public void registerObserver(Observer o) {
             this.observers.add(o);
         }
 
         @Override
         public void notifyObservers(String twee) {
            observers.forEach(o ->o.notify(twee));
         }
 
         // 实现观察方法
         public void main(){
             Feed f = new Feed();
             // 将不同的观察者放入观察者列表,具体的观察方法由实现类完成..
             f.registerObserver(new NYTimes());
             f.registerObserver(new Guardian());
             //
             f.notifyObservers("嗯. 来了一条特大新闻!!!!");
 
             // 使用Lambda表达式,将观察者的观察行为参数化
             f.registerObserver((String string) ->{
                 if (string != null){
                     System.out.println("这个新闻太水啦!" + string);
                 }
             });
             f.registerObserver((String string) ->{
                 if (string != null){
                     System.out.println("这个新闻挺好的!!" + string);
                 }
             });
 
         }
     }

回过头来看一下

观察者模式:在对象之间定义一对多的依赖,这样一来,当一个对象改变状态依赖它的对象都会收到通知,并自动更新。观察者模式的代表人物——·MVC··

GOF描述

Observer模式允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者,反之亦然。它也使你可以在不改动目标和其他的观察者的前提下增加观察者。下面是观察者模式其它一些优缺点:

  • 1)目标和观察者间的抽象松耦合一个目标所知道的仅仅是它有一系列观察者,每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。因为目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它,这样就保持了系统层次的完整。如果目标和观察者混在一块,那么得到的对象要么横贯两个层次(违反了层次性),要么必须放在这两层的某一层中(这可能会损害层次抽象)。
  • 2)支持广播通信不像通常的请求,目标发送的通知不需指定它的接收者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣;它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。
    在这里插入图片描述
  • 3)意外的更新 因为一个观察者并不知道其它观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外,如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。简单的更新协议不提供具体细节说明目标中什么被改变了,这就使得上述问题更加严重。如果没有其他协议帮助观察者发现什么发生了改变,它们可能会被迫尽力减少改变。*


命令(command)模式-对象行为型:

命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

封装调用:我们将把封装带到一个全新的境界:把方法调用(method invocation)封装起来
意图:将一个请求封装成一个象,从而使您可以用不同的请求对客户进行行为参数化。函数式编程,行为参数化,lambda表达式
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
应用实例:struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

情景:做一个家电自动化遥控器的API,不同按钮控制不同的家电行为。有一个整体的撤销功能,家电类特别多。正常的写法,我们的API里会嵌套很多的条件语句,使用命令模式可以解决我们的问题。命令模式可将动作的请求者动作的执行者对象中`解耦。利用命令对象,把请求(例如开电灯)封装成一个特定对象(例如客厅电灯对象) 。所以,如果对每个按钮都在储一个命令对象,那么当按钮被按下的时候,就可以请命令对象做相关的工作.遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。所以,遥控器和电灯对象解耦了。
在这里插入图片描述

基本命令模式

命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,·命令对象动作接收者包进对象中。这个对象只暴露出一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。
从外面来看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道如果调用execute()方法,请求的目的就能达到。我们也看到了利用命令来参数化对象的一些例子。在简单遥控器中,我们先用一个“打开电灯”命令加载按钮插槽,稍后又将命令替换成为另一个“打开车库门”命令。遥控器插槽根本不在乎所拥有的是什么命令对象,只要该命令对象实现了Command接口就可以了。我们还未说到使用命令模式来实现“队列、日志和支持撤销操作”。别担心,这是基本命令模式相当直接的扩展,很快我们就会看到这些内容。一旦有了足够的基础,也可以轻易地持所谓的Meta Command Pattern, Meta Command Pattern可以创建命令的宏,以便一次执行多个命令。
在这里插入图片描述

命令抽象及其实现类
/**
 * @Description : TODO 命令抽象
 * @author Liruilong
 * @Date 2021-05-17 17:57
 **/
public interface Command {

	public void execute();
}

public class LightOffCommand implements Command {
	Light light;
 
	public LightOffCommand(Light light) {
		this.light = light;
	}
 
	@Override
	public void execute() {
		light.off();
	}
}
public class LightOnCommand implements Command {
	Light light;
  
	public LightOnCommand(Light light) {
		this.light = light;
	}
 
	@Override
	public void execute() {
		light.on();
	}
}
public class GarageDoorOpenCommand implements Command {
	GarageDoor garageDoor;

	public GarageDoorOpenCommand(GarageDoor garageDoor) {
		this.garageDoor = garageDoor;
	}

	@Override
	public void execute() {
		garageDoor.up();
	}
}
接受者类
public class Light {

	public Light() {
	}

	public void on() {
		System.out.println("Light is on");
	}

	public void off() {
		System.out.println("Light is off");
	}
}
public class GarageDoor {

	public GarageDoor() {
	}

	public void up() {
		System.out.println("Garage Door is Open");
	}

	public void down() {
		System.out.println("Garage Door is Closed");
	}

	public void stop() {
		System.out.println("Garage Door is Stopped");
	}

	public void lightOn() {
		System.out.println("Garage light is on");
	}

	public void lightOff() {
		System.out.println("Garage light is off");
	}
}

遥控器类
/**
 * @Description : TODO 简单的遥控器
 * @author Liruilong
 * @Date 2021-05-17 17:12
 **/
public class SimpleRemoteControl {

	/**
	* slot 遥控器插槽
	*/
	Command slot;

	public SimpleRemoteControl() {}

	/**
	 * <per>
	 * <p>将行为注册到插槽</p>
	 * <per/>
	 *
	 * @param command
	 * @return void
	 * @throws
	 * @Description : TODO
	 * @author Liruilong
	 * @Date 2021-05-17 17:07
	 **/
	public void setCommand(Command command) {
		slot = command;
	}

	/**
	 * <per>
	 * <p>执行行为方法</p>
	 * <per/>
	 *
	 * @param
	 * @return void
	 * @throws
	 * @Description : TODO
	 * @author Liruilong
	 * @Date 2021-05-17 17:10
	 **/
	public void buttonWasPressed() {
		slot.execute();
	}
测试类
/**
 * @Description : TODO 一个命令模式的客户
 * @author Liruilong
 * @Date 2021-05-17 17:13
 **/
public class RemoteControlTest {
	public static void main(String[] args) {
		//简单的遥控器
		SimpleRemoteControl remote = new SimpleRemoteControl();
		// 灯的按钮命令
		Light light = new Light();
        // 车库门的开关命令
		GarageDoor garageDoor = new GarageDoor();
		//注册灯的命令
		LightOnCommand lightOn = new LightOnCommand(light);
		//注册车库门的命令
		GarageDoorOpenCommand garageOpen = new GarageDoorOpenCommand(garageDoor);
        // 把命令传给调用者
		remote.setCommand(lightOn);
		// 模拟遥控器点击
		remote.buttonWasPressed();
		remote.setCommand(garageOpen);
		remote.buttonWasPressed();
    }
	
}

java8的写法

命令通过行为参数化的方式,使用方法引用,或者lambda表达式的方式,只要符合Command 的函数签名即可 ()-void

@FunctionalInterface 
public interface Command {
	public void execute();
}

public class RemoteControlTest {
	public static void main(String[] args) {
		SimpleRemoteControl remote = new SimpleRemoteControl();
		Light light = new Light();
		GarageDoor garageDoor = new GarageDoor();
		remote.setCommand(light::on);
		remote.setCommand(() -> light.off());
		remote.buttonWasPressed();
		remote.setCommand(garageDoor::up);
		remote.buttonWasPressed();
		remote.setCommand(garageDoor::lightOn);
		remote.buttonWasPressed();
		remote.setCommand(garageDoor::lightOff);
		remote.buttonWasPressed();
    }
}
JDK 中的对于基本命令模式的应用

JDK 中的对于基本命令模式在Thread中有类似的使用,但是还是有些不同,个人理解,感觉不合理小伙伴们可以留言 ,使用Runnable接口构造的线程,会通过Runnable接口的run方法编写线程行为,通过Thread的start方法调用run方法(start方法调用start0(),然后start0()方法调用用run方法)。Runnable 接口可以看做是抽象命令类Command,run方法即为execcute()方法,Thread 类即为Invoker,使用start()方法调用run方法,具体的使用Runnable接口构造的线程体类即实现了动作(逻辑)和接受者(业务对象)的绑定,我们来看一个实例

package com.liruilong.design_pattern.command;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/*
 * @Description 实现了runnable接口的行为类
 * @author Liruilong
 * @date  2021/5/23  10:16
 **/
public class Out1 implements Runnable{
   // 接收者
    private DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
   // execute()
    @Override
    public void run() {
        System.out.println(LocalDate.now().format(pattern));
    }
}

package com.liruilong.design_pattern.command;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/*
 * @Description 实现了runnable接口的行为类
 * @author Liruilong
 * @date  2021/5/23  10:16
 **/
public class Out2  implements Runnable{
    // 接收者
    private DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

    @Override
    public void run() {
        System.out.println(LocalDateTime.now().format(pattern));
    }
}
package com.liruilong.design_pattern.command;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Main {

    public static void main(String[] args) {

        Runnable out1 = new Out1();
        Thread thread1 = new Thread(out1);
        thread1.start();

        Runnable out2 = new Out2();
        Thread thread2 = new Thread(out2);
        thread2.start();

        //java8的写法
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        DateTimeFormatter pattern_ = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        new Thread(() ->System.out.println(LocalDate.now().format(pattern))).start();
        new Thread(() -> System.out.println(LocalDateTime.now().format(pattern_))).start();

    }
}

撤销功能的处理

当命令支持撤销时,该命令就必须提供和execute()方法相反的undo()方法。不管execute()刚才做什么, undo()都会倒转过来。这么一来,在各个命令中加入undo()之前,我们必须先在Command接口中加入undo()方法:
在这里插入图片描述

抽象命令接口
public interface Command {
	public void execute();
	public void undo();
}
遥控器类
package headfirst.designpatterns.command.undo;

//
// This is the invoker
//
public class RemoteControlWithUndo {
	Command[] onCommands;
	Command[] offCommands;
	Command undoCommand;
 
	public RemoteControlWithUndo() {
		onCommands = new Command[7];
		offCommands = new Command[7];
 
		Command noCommand = new NoCommand();
		for(int i=0;i<7;i++) {
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
		undoCommand = noCommand;
	}
  
	public void setCommand(int slot, Command onCommand, Command offCommand) {
		onCommands[slot] = onCommand;
		offCommands[slot] = offCommand;
	}
	public void onButtonWasPushed(int slot) {
		onCommands[slot].execute();
		undoCommand = onCommands[slot];
	}
	public void offButtonWasPushed(int slot) {
		offCommands[slot].execute();
		undoCommand = offCommands[slot];
	}
	public void undoButtonWasPushed() {
		undoCommand.undo();
	}
}
使用状态实现撤销

通常,想要实现撤销·的功能,需要记录一些状态。让我们试一个更有趣的例子,比方说厂商类中的天花板上的吊扇。吊扇允许有多种转动速度,当然也允许被关闭。

package headfirst.designpatterns.command.undo;

public class CeilingFan {
	public static final int HIGH = 3;
	public static final int MEDIUM = 2;
	public static final int LOW = 1;
	public static final int OFF = 0;
	String location;
	int speed;
 
	public CeilingFan(String location) {
		this.location = location;
		speed = OFF;
	}
  
	public void high() {
		speed = HIGH;
		System.out.println(location + " ceiling fan is on high");
	} 
 
	public void medium() {
		speed = MEDIUM;
		System.out.println(location + " ceiling fan is on medium");
	}
 
	public void low() {
		speed = LOW;
		System.out.println(location + " ceiling fan is on low");
	}
  
	public void off() {
		speed = OFF;
		System.out.println(location + " ceiling fan is off");
	}
  
	public int getSpeed() {
		return speed;
	}
}
加入撤销到吊扇的命令类
package headfirst.designpatterns.command.undo;

public class CeilingFanHighCommand implements Command {
	CeilingFan ceilingFan;
	// 增加局部变量以追踪吊扇之前的速度
	int prevSpeed;
  
	public CeilingFanHighCommand(CeilingFan ceilingFan) {
		this.ceilingFan = ceilingFan;
	}
 
	public void execute() {
		prevSpeed = ceilingFan.getSpeed();
		ceilingFan.high();
	}
 
	public void undo() {
		if (prevSpeed == CeilingFan.HIGH) {
			ceilingFan.high();
		} else if (prevSpeed == CeilingFan.MEDIUM) {
			ceilingFan.medium();
		} else if (prevSpeed == CeilingFan.LOW) {
			ceilingFan.low();
		} else if (prevSpeed == CeilingFan.OFF) {
			ceilingFan.off();
		}
	}
}

使用宏命令,批量操作
package headfirst.designpatterns.command.party;

public class MacroCommand implements Command {
	Command[] commands;
 
	public MacroCommand(Command[] commands) {
		this.commands = commands;
	}
 
	public void execute() {
		for (int i = 0; i < commands.length; i++) {
			commands[i].execute();
		}
	}
 
    /**
     * NOTE:  these commands have to be done backwards to ensure 
     * proper undo functionality
     */
	public void undo() {
		for (int i = commands.length -1; i >= 0; i--) {
			commands[i].undo();
		}
	}
}

`问`:接收者一定有必要存吗?为何命令对象不直接实现execute()方法的细节?
`答`:一般来说,我们尽量设行“使瓜”命令对象,它只懂得调且一个接收者的一个行为。然而,许多“聪明”命令对象会实现许多逻辑,直接完成一个请求。当然你可发以设计聪明的命令对象,只是这样一未,调用者和接收者之间的解偶程度比不上“傻瓜”命令对象的,而且,你也不能够把接收者当做参数传

命令模式的更多用途:

队列请求

命令可以将运算块打包(一个接收者和一组动作)然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性衍生一些应用,例如:日程安排(Scheduler)线程池工作队列等。想象有一个工作队列:你在某一端添加命令,然后另端则是线程。线程进行下面的动作:从队列中取出一个命令,调用它的execute)方法,等待这个调用完成,然后将此命令对象丢弃,再取出下一个命令…**

日志请求

某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用些动作恢复到之前的状态。通过新增两个方法(store(), load()) ,命令模式能够支持这一点。在Java中,我们可以利用对象的序列化(Serialization)实现方法,但是一般认为序列化最好还是只用在对象的持久化上(persistence) 。

怎么做呢?当我们执行命令的时候,将历史记录储存在磁盘中。一旦系统死机,可以将命令对象重新加载,并成批地依次调用这些对象的execute0方法。

日志的方式对于遥控器来说没有意义,然而,有许多调用大型数据结构的动作的应用无法在每次改变发生时被快速地存储。通过使用记录日志,我们可以上次检查点(checkpoint)之后的所有操作记录下来,如果系统出状况,从检查点开始应用这些操作。比方说,对于电子表格应用,我们可能想要实现的错恢复方式是将电子表格的操作记录在日志中,而不是每次电子表格一有变化就记录整个电子表格。对更高级的应用而言,这些技巧可以被扩展应用到事务tansaction)理中,也就是说,一整群操作必须全部进行完成,或者没有进行任何的操作。
在这里插入图片描述

命令模式将发出请求的对象和执行请求的对象解耦,在被解耦的两者直接是通过命令对象进行沟通的,命令对象封装了接受者和一个或者一组动作,调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用,调用者可以接受命令当做参数,甚至在运行时动态的进行,命令可以撤销,实现的是一个叫undo()方法来回到execute()被执行前的状态,宏命令是命令的

回顾一下定义:

命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。,这可以让你使用不同的请求、队列,或者日志请求来と参数化其他对象。命令模式也可以支持微销操作。

GOF描述

Command模式有以下效果:

  • Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
  • Command是头等的对象。它们可像其他的对象一样被操纵和扩展。
  • 你可将多个命令装配成一个复合命令。例如是前面描述的MacroCommand类。一般说来,复合命令是Composite模式的一个实例。
  • 增加新的Command很容易,因为这无需改变已有的类。
    在这里插入图片描述


访问者(Visitor)模式-对象行为型

访问者模式(Visitor Pattern)中,使用一个访问者类,改变元素类执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

意图:主要将 数据结构 数据操作分离
主要解决稳定的数据结构易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将·自身引用传入访问者

应用实例您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

实现
我们将创建一个定义接受操作的 ComputerPart 接口。KeyboardMouseMonitor  Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。
在这里插入图片描述

定义一个表示元素的接口。ComputerPart.java
public interface ComputerPart {
   +void accept(ComputerPartVisitor computerPartVisitor);
}
创建扩展了上述类的实体类。Keyboard.java
public class Keyboard  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
Monitor.java
public class Monitor  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
Mouse.java
public class Mouse  implements ComputerPart {
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}
Computer.java
public class Computer implements ComputerPart {
   
   ComputerPart[] parts;
 
   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};      
   } 
 
 
   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}
定义一个表示访问者的接口。ComputerPartVisitor.java
public interface ComputerPartVisitor {
   public void visit(Computer computer);
   public void visit(Mouse mouse);
   public void visit(Keyboard keyboard);
   public void visit(Monitor monitor);
}
创建实现了上述类的实体访问者。ComputerPartDisplayVisitor.java
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
 
   @Override
   public void visit(Computer computer) {
      System.out.println("Displaying Computer.");
   }
 
   @Override
   public void visit(Mouse mouse) {
      System.out.println("Displaying Mouse.");
   }
 
   @Override
   public void visit(Keyboard keyboard) {
      System.out.println("Displaying Keyboard.");
   }
 
   @Override
   public void visit(Monitor monitor) {
      System.out.println("Displaying Monitor.");
   }
}
使用 ComputerPartDisplayVisitor 来显示 Computer 的组成部分。VisitorPatternDemo.java
public class VisitorPatternDemo {
   public static void main(String[] args) {
      ComputerPart computer = new Computer();
      computer.accept(new ComputerPartDisplayVisitor());
   }
}
执行程序,输出结果:
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.

GOF描述

1)访问者模式使得易于增加新的操作 访问者使得增加依赖于复杂对象结构的构件的操作变得容易了。仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作。相反,如果每个功能都分散在多个类之上的话,定义新的操作时必须修改每一类
2)访问者集中相关的操作而分离无关的操作 相关的行为不是分布在定义该对象结构的各个类上,而是集中在一个访问者中。无关行为却被分别放在它们各自的访问者子类中。这就既简化了这些元素的类,也简化了在这些访问者中定义的算法。所有与它的算法相关的数据结构都可以被隐藏在访问者中。

在这里插入图片描述
3)增加新的ConcreteElement类很困难 Visitor模式使得难以增加新的Element的子类。每添加一个新的ConcreteElement都要在Vistor中添加一个新的抽象操作,并在每一个ConcretVisitor类中实现相应的操作。有时可以在Visitor中提供一个缺省的实现,这一实现可以被大多数的ConcreteVisitor继承,但这与其说是一个规律还不如说是一种例外。所以在应用访问者模式时考虑关键的问题是系统的哪个部分会经常变化,是作用于对象结构上的算法呢还是构成该结构的各个对象的类。如果老是有新的ConcretElement类加入进来的话, Vistor类层次将变得难以维护。在这种情况下,直接在构成该结构的类中定义这些操作可能更容易一些。如果Element类层次是稳定的,而你不断地增加操作获修改算法,访问者模式可以帮助你管理这些改动。
4)通过类层次进行访问一个迭代器(参见Iterator)可以通过调用节点对象的特定操作来遍历整个对象结构,同时访问这些对象。但是迭代器不能对具有不同元素类型的对象结构进行操作。例如,定义在第5章的Iterator接口只能访问类型为Item的对象:

在这里插入图片描述



模式之间的区别

设计模式的划分更多的是面对问题域而不是面对实现方式,实际上所有模式可以只分为类模式对象模式两种,类模式是用继承对象模式是用委托Bridge模式和Strategy模式相似就是因为他们都将任务委托给了另外一个接口的具体实现
在这里插入图片描述

他们之间的区别在于Bridge的目的是让底层实现和上层接口可以分别演化,从而提高移植性而Strategy的目的是将复杂的算法封装起来,从而便于替换不同的算法。因此可以想象一般情况下Bridge的实现几乎不会在运行时更改而Strategy的算法则很有可能需要在运行时更换,这就导致在细节方面需要考虑的因素可能会很不相同。

strategy模式是为了扩展和修改,并提供动态配置。它往往可以在同一环境当中使用不同的策略,就是调用不同的派生类。其内部实现是自由的,不受已有的类接口的限制(很多时候根本就不调用现成的接口)。
bridge模式是往往是为了利用已有的方法或类。它将原来不统一,不兼容的接口封装起来,变成统一的接口。它的应用往往是不同的环境或平台下只能选择一 种,比如说在windows平台下只能用WinClass,而在unix平台下只能用UnixClass.它的主要作用不是配置而是定义通用接口。

据个例子来说:我要画园,要实心园,我可以用SolidPen来配置,画虚线园可以用dashedPen来配置。这是strategy模式。而同样是画园,我是在windows下来画实心园,就用windowPen+solidPen来配置,在unix下画实心园就用 unixPen+solidPen来配置。如果要再windows下画虚线园,就用windowsPen+dashedPen来配置,要在unix下画虚 线园,就用unixPen+dashedPen来配置。

我这里仅仅是就一种情况来说strategy和bridge的组合应用,其他的组合可能性随环境变化而多种多样。从中可以看出,bridge和strategy是可能组合使用,侧重不同方面的。模式某种角度上来讲就是对象组合。不要看他们都是对象组合就好像是一样的。模式的动机,意图,使用场合,组合方式,这些都是模式的一部分。其中细微的不同足以区分不同的模式。

在桥接模式中,Abstraction通过聚合的方式引用Implementor。
在策略模式中,Context也使用聚合的方式引用Startegy抽象接口。

从他们的结构图可知,在这两种模式中,都存在一个对象使用聚合的方式引用另一个对象的抽象接口的情况,而且该抽象接口的实现可以有多种并且可以替换。可以说两者在表象上都是调用者与被调用者之间的解耦,以及抽象接口与实现的分离。

那么两者的区别体现在什么地方呢?
  1. 首先,在形式上,两者还是有一定区别的,对比两幅结构图,我们可以发现,在桥接模式中不仅Implementor具有变化 (ConcreateImplementior),而且Abstraction也可以发生变化(RefinedAbstraction),而且两者的变化 是完全独立的,RefinedAbstraction与ConcreateImplementior之间松散耦合,它们仅仅通过Abstraction与 Implementor之间的关系联系起来。而在策略模式中,并不考虑Context的变化,只有算法的可替代性。

  2. 其次在语意上,桥接模式强调Implementor接口仅提供基本操作,而Abstraction则基于这些基本操作定义更高层次的操作。而策略模式强调 Strategy抽象接口的提供的是一种算法,一般是无状态、无数据的,而Context则简单调用这些算法完成其操作。

  3. 桥接模式中不仅定义Implementor的接口而且定义Abstraction的接口,Abstraction的接口不仅仅是为了与 Implementor通信而存在的,这也反映了结构型模式的特点:通过继承、聚合的方式组合类和对象以形成更大的结构。在策略模式中,Startegy 和Context的接口都是两者之间的协作接口,并不涉及到其它的功能接口,所以它是行为模式的一种。行为模式的主要特点就是处理的是对象之间的通信方 式,往往是通过引入中介者对象将通信双方解耦,在这里实际上就是将Context与实际的算法提供者解耦。

所以相对策略模式,桥接模式要表达的内容要更多,结构也更加复杂。桥接模式表达的主要意义其实是接口隔离的原则,即把本质上并不内聚的两种体系区别开来,使得它们可以松散的组合,而策略在解耦上还仅仅是某一个算法的层次,没有到体系这一层次。从结构图中可以看到,策略的结构是包容在桥接结构中的,桥 接中必然存在着策略模式,Abstraction与Implementor之间就可以认为是策略模式,但是桥接模式一般Implementor将提供一系 列的成体系的操作,而且Implementor是具有状态和数据的静态结构。而且桥接模式Abstraction也可以独立变化。


Adapter(适配器)与Bridgede(桥接模式) 的区别

Adapter 模式和Bridge 模式具有一些共同的特征:

它们都给另一对象提供了一定程度上的间接性,因而有利于系统的灵活性。它们都涉及到从自身以外的一个接口向这个对象转发请求。

不同之处主要在于它们各自的途:
  • Adapter模式主要是为了解决两个已有接口之间不匹配的问题。它不考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。这种方式不需要对两个独立设计的类中的任一个进行重新设计,就能够使它们协同工作。**
  • Bridge模式则对抽象接口与它的(可能是多个)实现部分进行桥接。虽然这一模式允许你修改实现它的类,它仍然为用户提供了一个稳定的接口。Bridge模式也会在系统演化时适应新的实现。
Adapter和Bridge模式通常被用于软件生命周期的不同阶段

当你发现两个不兼容的类必须同时工作时,就有必要使用Adapter模式,其目的一般是为了避免代码重复。此处耦合不可预见。相反, Bridge的使用者必须事先知道:一个抽象将有多个实现部分,并且抽象和实现两者是独立演化的。Adapter模式在类已经设计好后实施;而Bridge模式在设计类之前实施。这并不意味着Adapter模式不如Bridge模式,只是因为它们针对了不同的问题。你可能认为facade(参见Facade(4.5))是另外一组对象的适配器。但这种解释忽视了一个事实:即Facade定义一个新的接口,而Adapter则复用一个原有的接口。记住,适配器使两个已有的接口协同工作,而不是定义一个全新的接口


Adapter(适配器)和Facade(外观)的区别:

:我可不可以这样说,适配器模式外观模式之间的差异在于:适配器包装一个类,而外观可以代表许多类?
:不对!提醒你、适配器模式将一个或多个类接口变成客户所期望的,一个接口。虽然大多数教科书所采用的例子中适配器只适配一个类,但是你可以适配许多类来提供一个接口让客户编码。类似地,一个外观也可以只针对一个拥有复杂接口的类提供简化的接口。两种模式的差异,不在于它们“包装”了几个类,而是在于它们的意图,适配器模式的意图是, “改变”接口符合客户的期望;而外观模式的意图是,提供子系统的一个简化接口。
适配器将一个对象包装起来改变其接口,装饰者将一个对象包装起来以增加其他行为和责任,而外观模式将一群对象“包装”起来以简化其接口。


Composite, Decorator与Proxy的区别

Composite(4.3)模式和Decorator(4.4)模式具有类似的结构图,这说明它们都基于递归组合来组织可变数目的对象。这一共同点可能会使你认为, decorator对象是一个退化的composite,但这一观点没有领会Decorator模式要点。相似点仅止于递归组合,同样,这是因为这两个模式的目的不同。
Decorator旨在使你能够不需要生成子类即可给对象添加职责。这就避免了静态实现所有功能组合,从而导致子类急剧增加。Composite则有不同的目的,它旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理。它重点不在于修饰,而在于表示。
尽管它们的目的截然不同,但却具有互补性。因此Composite和Decorator模式通常协同使用。在使用这两种模式进行设计时,我们无需定义新的类,仅需将一些对象插接在一起即可构建应用。这时系统中将会有一个抽象类,它有一些composite子类和decorator子类,还有一些实现系统的基本构建模块。此时, composites和decorator将拥有共同的接口。从Decorator模式的角度看, composite是一个ConcreteComponent。而从composite模式的角度看,decorator则是一个Leaf。当然,他们不一定要同时使用,正如我们所见,它们的目的有很大的差别。

另一种与Decorator模式结构相似的模式是Proxy(4.7),这两种模式都描述了怎样为对象提供一定程度上的间接引用, proxy和decorator对象的实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求。然而同样,它们具有不同的设计目的。

像Decorator模式一样, Proxy模式构成一个对象并为用户提供一致的接口。但与Decorator模式不同的是, Proxy模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者,例如,实体在远程设备上,访问受到限制或者实体是持久存储的。

在Proxy模式中,实体定义了关键功能,而Proxy提供(或拒绝)对它的访问。在Decorator模式中,组件仅提供了部分功能,而一个或多个Decorator负责完成其他功能。Decorator模式适用于编译时不能(至少不方便)确定对象的全部功能的情况。这种开放性使递归组合成为Decorator模式中一个必不可少的部分。而在Proxy模式中则不是这样,因为Proxy模式强调一种关系(Proxy与它的实体之间的关系),这种关系可以静态的表达。

模式间的这些差异非常重要,因为它们针对了面向对象设计过程中一些特定的经常发生问题的解决方法。但这并不意味着这些模式不能结合使用。可以设想有一个proxy-decorator,它可以给proxy添加功能,或是一个decorator-proxy用来修饰一个远程对象。尽管这种混合可能有用(我们手边还没有现成的例子),但它们可以分割成一些有用的模式。

四,23种设计模式之外的设计模式

空对象(Null Object)模式

空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。
实现
我们将创建一个定义操作(在这里,是客户的名称)的 AbstractCustomer 抽象类,和扩展了 AbstractCustomer 类的实体类。工厂类 CustomerFactory 基于客户传递的名字来返回 RealCustomer 或 NullCustomer 对象。
NullPatternDemo,我们的演示类使用 CustomerFactory 来演示空对象模式的用法。
在这里插入图片描述

创建一个抽象类。AbstractCustomer.java
public abstract class AbstractCustomer {
   protected String name;
   public abstract boolean isNil();
   public abstract String getName();
}
创建扩展了上述类的实体类。RealCustomer.java
public class RealCustomer extends AbstractCustomer {
 
   public RealCustomer(String name) {
      this.name = name;    
   }
   
   @Override
   public String getName() {
      return name;
   }
   
   @Override
   public boolean isNil() {
      return false;
   }
}
NullCustomer.java
public class NullCustomer extends AbstractCustomer {
 
   @Override
   public String getName() {
      return "Not Available in Customer Database";
   }
 
   @Override
   public boolean isNil() {
      return true;
   }
}
`创建 CustomerFactory 类。CustomerFactory.java```
public class CustomerFactory {
   
   public static final String[] names = {"Rob", "Joe", "Julie"};
 
   public static AbstractCustomer getCustomer(String name){
      for (int i = 0; i < names.length; i++) {
         if (names[i].equalsIgnoreCase(name)){
            return new RealCustomer(name);
         }
      }
      return new NullCustomer();
   }
}
使用 CustomerFactory,基于客户传递的名字,来获取 RealCustomer 或 NullCustomer 对象。NullPatternDemo.java
public class NullPatternDemo {
   public static void main(String[] args) {
      //从工厂里面获取对象
      AbstractCustomer customer1 = CustomerFactory.getCustomer("Rob");
      AbstractCustomer customer2 = CustomerFactory.getCustomer("Bob");
      AbstractCustomer customer3 = CustomerFactory.getCustomer("Julie");
      AbstractCustomer customer4 = CustomerFactory.getCustomer("Laura");
 
      System.out.println("Customers");
      System.out.println(customer1.getName());
      System.out.println(customer2.getName());
      System.out.println(customer3.getName());
      System.out.println(customer4.getName());
   }
}
执行程序,输出结果:
Customers
Rob
Not Available in Customer Database
Julie
Not Available in Customer Database

JDK1.8 中的Null Object 模式的使用 Optional

Optional 对象可能包含或不包含非空值的容器对象。 如果一个值存在, isPresent()将返回true和get()将返回值。
提供依赖于存在或不存在包含值的其他方法,例如orElse()/orElseGet() (如果值不存在则返回默认值)和ifPresent() (如果值存在则执行代码块)。

/*
package java.util;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
....
 * @since 1.8
 */
public final class Optional<T> {
   
    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    public boolean isPresent() {
        return value != null;
    }
    //Consumer : (T) -> void
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
     //Predicate : (T) -> boolean
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    // Function (T) -> R
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    public T orElse(T other) {
        return value != null ? value : other;
    }

   // Supplier  ()-> T
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
    // Supplier  ()-> T
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
    // 父类方法覆写
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Optional)) {
            return false;
        }

        Optional<?> other = (Optional<?>) obj;
        return Objects.equals(value, other.value);
    }
    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }
    @Override
    public String toString() {
        return value != null? String.format("Optional[%s]", value) : "Optional.empty";
    }
}

过滤器(Filter)模式或标准(Criteria)模式

允许开发人员使用不同的标准过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它`结合多个标准来获得单一标准。
这个很容易理解,不多解释。

实现:我们将创建一个 Person 对象、Criteria 接口和实现了该接口的实体类,来过滤 Person 对象的列表。CriteriaPatternDemo 类使用 Criteria 对象,基于各种标准和它们的结合来过滤 Person 对象的列表。
在这里插入图片描述
Person.java

public class Person {
   
   private String name;
   private String gender;
   private String maritalStatus;
 
   public Person(String name,String gender,String maritalStatus){
      this.name = name;
      this.gender = gender;
      this.maritalStatus = maritalStatus;    
   }
 
   public String getName() {
      return name;
   }
   public String getGender() {
      return gender;
   }
   public String getMaritalStatus() {
      return maritalStatus;
   }  
}

Criteria.java

import java.util.List;
 
public interface Criteria {
   public List<Person> meetCriteria(List<Person> persons);
}

CriteriaMale.java

import java.util.ArrayList;
import java.util.List;
 
public class CriteriaMale implements Criteria {
 
   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> malePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getGender().equalsIgnoreCase("MALE")){
            malePersons.add(person);
         }
      }
      return malePersons;
   }
}

CriteriaFemale.java

import java.util.ArrayList;
import java.util.List;
 
public class CriteriaFemale implements Criteria {
 
   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> femalePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getGender().equalsIgnoreCase("FEMALE")){
            femalePersons.add(person);
         }
      }
      return femalePersons;
   }
}

CriteriaSingle.java

import java.util.ArrayList;
import java.util.List;
 
public class CriteriaSingle implements Criteria {
 
   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> singlePersons = new ArrayList<Person>(); 
      for (Person person : persons) {
         if(person.getMaritalStatus().equalsIgnoreCase("SINGLE")){
            singlePersons.add(person);
         }
      }
      return singlePersons;
   }
}

AndCriteria.java

import java.util.List;
 
public class AndCriteria implements Criteria {
 
   private Criteria criteria;
   private Criteria otherCriteria;
 
   public AndCriteria(Criteria criteria, Criteria otherCriteria) {
      this.criteria = criteria;
      this.otherCriteria = otherCriteria; 
   }
 
   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> firstCriteriaPersons = criteria.meetCriteria(persons);     
      return otherCriteria.meetCriteria(firstCriteriaPersons);
   }
}

OrCriteria.java

import java.util.List;
 
public class OrCriteria implements Criteria {
 
   private Criteria criteria;
   private Criteria otherCriteria;
 
   public OrCriteria(Criteria criteria, Criteria otherCriteria) {
      this.criteria = criteria;
      this.otherCriteria = otherCriteria; 
   }
 
   @Override
   public List<Person> meetCriteria(List<Person> persons) {
      List<Person> firstCriteriaItems = criteria.meetCriteria(persons);
      List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons);
 
      for (Person person : otherCriteriaItems) {
         if(!firstCriteriaItems.contains(person)){
           firstCriteriaItems.add(person);
         }
      }  
      return firstCriteriaItems;
   }
}
import java.util.ArrayList; 
import java.util.List;
 
public class CriteriaPatternDemo {
   public static void main(String[] args) {
      List<Person> persons = new ArrayList<Person>();
 
      persons.add(new Person("Robert","Male", "Single"));
      persons.add(new Person("John","Male", "Married"));
      persons.add(new Person("Laura","Female", "Married"));
      persons.add(new Person("Diana","Female", "Single"));
      persons.add(new Person("Mike","Male", "Single"));
      persons.add(new Person("Bobby","Male", "Single"));
 
      Criteria male = new CriteriaMale();
      Criteria female = new CriteriaFemale();
      Criteria single = new CriteriaSingle();
      Criteria singleMale = new AndCriteria(single, male);
      Criteria singleOrFemale = new OrCriteria(single, female);
 
      System.out.println("Males: ");
      printPersons(male.meetCriteria(persons));
 
      System.out.println("\nFemales: ");
      printPersons(female.meetCriteria(persons));
 
      System.out.println("\nSingle Males: ");
      printPersons(singleMale.meetCriteria(persons));
 
      System.out.println("\nSingle Or Females: ");
      printPersons(singleOrFemale.meetCriteria(persons));
   }
 
   public static void printPersons(List<Person> persons){
      for (Person person : persons) {
         System.out.println("Person : [ Name : " + person.getName() 
            +", Gender : " + person.getGender() 
            +", Marital Status : " + person.getMaritalStatus()
            +" ]");
      }
   }      
}

嗯,更新中…生活加油





在这里插入图片描述

模式
装饰者 将一个接口转成另一个接
适配器 不改变接口,但加入责任
外观 让接口更简单
模板方法 封装可互换的行为,然后使用委托来决定要采用哪一个行为
策略 子类决定如何实现算法中的
工厂方法 由子类决定实例化哪个
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。