Java设计模式初探之二

此文章承接上文设计模式里的结构型,如有疑惑,请移步上一篇:https://www.jianshu.com/p/1a2bc93cd929

外观模式:

外观模式就是封装子系统的复杂组成,对外提供一个门面角色,客户端通过门面来操作系统,对内部子系统无知,子系统可以有多个,每个可以独立运行,可以被外部直接调用,对于子系统而言,门面角色也相当于客户端

例如:

public class ActionFace implements IModule{

    private IModule action1 = new ModuleA();
    private IModule action2 = new ModuleB();
    private IModule action3 = new ModuleC();

    @Override
    public void action() {
        action1.action();
        action2.action();
        action3.action();
    }
}

这里定义action1,action2,action3模块,总共有3个模块,使用一个类ActionFace来提供action方法,这样外部可以通过调用该方法来同时调用这3个模块,当然了,这3个模块也可以被单独调用,ActionFace只是相当于一个封装

组合模式:

这个模式一般什么情况下面使用呢,就是整体和单体相似,就像树形结构,举个例子,好比文件夹系统,文件夹和文件相似,构成一个树形结构的文件系统,存在一个root节点,文件夹和文件属性相似,这就可以构成组合

举个例子:

定义一个抽象类:

public abstract class Component {
    protected String name;

    public void setName(String name) {
        this.name = name;
    }
}

这个类有2个类组合用于形成树形结构:

public class RootComponent extends Component {

    private List list = new ArrayList<>();

    public void add(Component left){
        list.add(left);
    }
}
public class LeftComponent extends Component {

    private String leftProperty;
}

下一步就是组合这2个类:

public static void main(String[] args){
    RootComponent root = new RootComponent();
    Component item = new LeftComponent("left");
    Component item2 = new LeftComponent("right");

    Component root3 = new RootComponent();

    root.add(item);
    root.add(item2);
    root.add(root3);
}

创建root根类,然后添加子元素或者子节点,这个例子就如文件系统,部分和整体类似,所以可以整合在一起

享元模式

这个模式比较简单,就是在一个集合中拿出对象,如果集合里面没有,那么新建这个对象并放入这个集合里面,然后再给外面使用

定义如下:

public class SharedPool {
    private HashMap map = new HashMap<>();

    public Object getEntity(T key){
        if(map.containsKey(key)) {
            return map.get(key);
        }else{
            Object o = new Object();
            map.put(key,o);
            return o;
        }
    }
}

这是一个根据key来查找存贮的值,如果不存在,新建加入Map然后返回该对象,当然了,这个可以定义成单利来存贮,不过请注意及时释放,不然会有内存泄漏哦~~

桥接模式:

这个设计模式目前自己也没有看懂。。。

行为型

责任链模式:

该设计模式个人理解为数据结构里面的单链表,每个元素都可能对客户端进行处理,如果一旦被消费了,那么即刻返回,当然也可能存在没有可以处理的请求

举个栗子:

定义一个抽象类,单链表里面每个元素构成如下:

public abstract class IHandler {
    private IHandler handler;
    public final void setNextHandler(IHandler handler) {
        this.handler = handler;
    }

    public final IHandler getNextHandler() {
        return handler;
    }

    public final boolean handleRequest(String args) {
        if(TextUtils.isEmpty(args)) {
            return onRequestHandle(args);
        }else{
            if(null != getNextHandler()) {
                return getNextHandler().handleRequest(args);
            }
        }
        return false;
    }

    abstract boolean onRequestHandle(String args);
}

public class DutyHandler extends IHandler{

    @Override
    boolean onRequestHandle(String args) {
        //handle request return true

        //otherwise return false visit next handle
        return false;
    }
}

链表里面每个子元素都是DutyHandler,使用setNextHandle保存下一个元素,下面我们编写测试:

public static void main(String[] args){
    IHandler topHandler = new DutyHandler();
    IHandler nextHandler = new DutyHandler();
    topHandler.setNextHandler(nextHandler);

    IHandler  nextHandler2= new DutyHandler();
    nextHandler.setNextHandler(nextHandler2);

    topHandler.handleRequest("");
}

这个例子创建的链表是这样的 topHandler -- nextHandler --nextHandler2

发送请求就会依次由头到尾调用handleRequest处理,如果成功,那么处理结束,否则查找下一个handler进行处理这个请求,但不保证一定能有链表元素能够处理

命令模式:

命令模式就是对接口实现体的一个分包装,将接口使用不同类引用并在实现体里面调用引用方法,这样可以将客户端与实现体分离,起到解耦作用,举个例子,假设有一个音乐播放器,有播放,停止,快进,返回等等,我们定义一个实现体:

public class AudioPlayer {
    public void play(){
        //play
    }

    public void pause(){
        //pause
    }

    public void stop(){
        //stop
    }

    public void skip(){
        //skip
    }
}

然后定义我们的客户端:

private ActionCommand pause,play,stop,skip;

private void test(){
    AudioPlayer player = new AudioPlayer();
    pause = new AudioPause(player);
    play = new AudioPlay(player);
    stop = new AudioStop(player);
    skip = new AudioSkip(player);

    pause.execute();
    play.execute();
    stop.execute();
    skip.execute();
}

这时候我们发现直接引用了AudioPlayer这个类型,如果需要添加新的功能,那么要修改这2块代码,产生了较高的耦合,下面我们用命令模式,让AudioPlayer对客户端不可见

  1. 定义接口:
public interface ActionCommand {
    void execute();
}
  1. 添加实现类:

2.1 播放方法的实现类

public class AudioPlay implements ActionCommand{
    private AudioPlayer player;

    public AudioPlay(AudioPlayer player) {
        this.player = player;
    }

    @Override
    public void execute() {
        player.play();
    }
}

2.2 暂停方法的实现类

public class AudioPause implements ActionCommand{
    private AudioPlayer player;

    public AudioPause(AudioPlayer player) {
        this.player = player;
    }

    @Override
    public void execute() {
        player.pause();
    }
}

2.3 停止实现类

public class AudioStop implements ActionCommand{
    private AudioPlayer player;

    public AudioStop(AudioPlayer player) {
        this.player = player;
    }

    @Override
    public void execute() {
        player.stop();
    }
}

2.4 跳过实现类

public class AudioSkip implements ActionCommand{
    private AudioPlayer player;

    public AudioSkip(AudioPlayer player) {
        this.player = player;
    }

    @Override
    public void execute() {
        player.skip();
    }
}
  1. 解耦
private ActionCommand pause,play,stop,skip;

private void test(){
    AudioPlayer player = new AudioPlayer();
    pause = new AudioPause(player);
    play = new AudioPlay(player);
    stop = new AudioStop(player);
    skip = new AudioSkip(player);

    pause.execute();
    play.execute();
    stop.execute();
    skip.execute();
}

使用这样可以看出,最终对AudioPlayer进行不同类型包装,包装之后每个类型都是执行execute让后对应执行AudioPlayer相同方法,这样修改AudioPlayer不会对client产生影响,但是缺点就是定义了过多的接口实现类和指令代码

解释器模式:

感觉没遇到过,不做讨论

迭代模式:

这种设计模式主要讲解关于如何进行数据的迭代查找,用于遍历数组中的元素,设计需要实现Iterable与Iterator接口,其实就是学习这2个接口怎么使用,举例如下:

public class CustomMap implements Iterable{

    private E[] array = null;
    private int index = 0;

    public CustomMap(E[] array) {
        this.array = array;
        index = 0;
    }

    @NonNull
    @Override
    public Iterator iterator() {
        return new CustomIterator();
    }

    private class CustomIterator implements Iterator{

        @Override
        public boolean hasNext() {
            return index < array.length;
        }

        @Override
        public E next() {
            try {
                return array[index];
            }finally {
                index ++;
            }
        }
    }
}

编写测试方法:

public static void main(String[] args){
    Integer[] result = new Integer[]{1,2,5,9,77,51};
    CustomMap map = new CustomMap<>(result);
    for (Integer i : map) {
        System.out.println(String.valueOf(i));
    }
}

上面的例子就是对一个数组封装然后实现遍历接口,这样就可以用for来迭代遍历。

中介者模式

该设计模式使用情况比较少,一般用于一个类影响多个类的情况,这种情况下,可能随便一个类都会影响其他多个类,这时候采取一个中介来控制其他类型,用于在一个类改变之后通过中介引用其他类来控制其他类变化

举个例子:假如有一个College类,有5个实例分别是abcde,当a改变的时候bce改变,当b改变的时候ace改变,当d改变的时候e改变,其他不变,那么我们可以这样做:

public class College {
    int value;

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
public abstract class IAnency {

    College collegea,collegeb,collegec,colleged,collegee;

    public IAnency(College collegea, College collegeb, College collegec, College colleged, College collegee) {
        this.collegea = collegea;
        this.collegeb = collegeb;
        this.collegec = collegec;
        this.colleged = colleged;
        this.collegee = collegee;
    }

    abstract void setAnency(College target,College... others);
}

定义抽象类,添加抽象方法setAnency,然后定义实现类,第一个参数是改变的成员,后面可变数组是真正需要因第一个参数改变而改变的成员变量

备忘录模式

备忘录模式就是对原对象存储当前节点的状态,用于在以后起到恢复,这个设计模式有点像创建型里面的原型模式,但是原型模式是拷贝对象,这里的设计模式是对某个对象创建状态存储类,然后在合适的时候取出该对象然后执行覆盖恢复
举例:
假设有一个SnapEntity类,里面有statue变量,在某个时刻需要对这个类备份,用于以后需要的时候恢复:

public interface ISnap {
    //empty interface
}
public class SnapEntity {
    private int statue;

    public ISnap createEntity(){
        return new SnapShot(this.statue);
    }

    public void restoreSnapEntity(ISnap snapShot){
        statue = ((SnapShot)snapShot).statue;
    }

    public void setStatue(int statue) {
        this.statue = statue;
    }

    public int getStatue() {
        return statue;
    }

    static class SnapShot implements ISnap{
        private int statue;

        public SnapShot(int statue) {
            this.statue = statue;
        }

        public int getStatue() {
            return statue;
        }
    }
}

这里定义一个空接口用于恢复的时候强转恢复里面的数据,这样的好处就是外部不能直接访问备份文件,之后在执行变量恢复方法的时候才能访问
下面我们创建一个类来保存备份数据:

public class CareTaker {
    private ISnap snapShot;

    public void setSnapShot(ISnap snapShot) {
        this.snapShot = snapShot;
    }

    public ISnap getSnapShot() {
        return snapShot;
    }
}

创建测试方法:

public static void main(String[] args){
    SnapEntity entity = new SnapEntity();
    entity.setStatue(3);
    CareTaker taker = new CareTaker();
    taker.setSnapShot(entity.createEntity());
    entity.setStatue(7);
    entity.restoreSnapEntity(taker.getSnapShot());
    System.out.print(String.valueOf(entity.getStatue()));
}

这是一个将之前保存的数据3保存然后再修改之后进行恢复的测试,这就是备忘录模式,当然了,实际情况下一个类型可能有很多变量,我们可以按需来设计或者进行备份,然后再设计一个享元模式的缓存池或者存储。

观察者模式:

这种设计模式都已经用惯了,不多介绍,嘿嘿~

状态模式:

这种设计模式就是对一个行为有不同的实现类来处理,通过不同case来产生接口实现类,每个实现类有特点的实现机制,这就好比switch case语句,这里举个例子:
定义一个接口:

public interface IStatueHandler {
    void handleStatue(int args);
}

接口有3个实现类,A,B,C,这3个实现类实现不同方法,然后编写测试方法:

public void handleStatue(int what){
    IStatueHandler handler = null;
    if(0 < what && what < 11) {
        handler = new StatueHandlerA();
    }else if(what < 51){
        handler = new StatueHandlerB();
    }else{
        handler = new StatueHandlerC();
    }
    handler.handleStatue(what);
}

这就是一个状态模式的简单例子,其实就是一个接口不同实现,然后case不同情况进行处理,和下面将要提到的策略模式一样。

策略模式:

该设计模式很像状态模式,同样定义公共接口,然后每个接口有不同的实现策略,并且有且只有一个策略(实现类)可以处理请求,这些策略(实现类)可以被及时替换,耦合性低,这里不再介绍。

模板方法模式:

这种设计模式就是定义抽象类,然后提供抽象方法,在实现具体方法里面提供核心抽象方法由子类实现,这里不再举例

你可能感兴趣的:(Java设计模式初探之二)