Android日积月累系列之九-设计模式-行为型模式

70 阅读35分钟

5.行为型模式

5.1 模板方法模式 Template Pattern

5.1.1基本介绍

**意图:**定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

**主要解决:**一些方法通用,却在每一个子类都重新写了这一方法。

**何时使用:**有一些通用的方法。

**如何解决:**将这些通用算法抽象出来。

**关键代码:**在抽象类实现,其他步骤在子类实现。

应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。

优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。

**缺点:**每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。

**注意事项:**为防止恶意操作,一般模板方法都加上 final 关键词。

  1. 基本思想是:**算法只存在于一个地方,也就是在父类中,容易修改。**需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改

  2. **实现了最大化代码复用。**父类的模板方法和已实现的某些步骤会被子类继承而直接使用。

  3. **既统一了算法,也提供了很大的灵活性。**父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。

  4. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大

  5. 一般模板方法都加上final关键字, 防止子类重写模板方法.

  6. 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理

5.1.2 模板方法模式里的钩子方法

方式1:最简单的钩子方法就是空方法,当然也可以在钩子方法中定义一个默认的实现,如果子类不覆盖钩子方法,则执行父类的默认实现代码。

方式2:另一种钩子方法可以实现对其他方法进行约束,这种钩子方法通常返回一个 bool 类型,即返回 true 或 false,用来判断是否执行某一个基本方法,下面通过一个实例来说明这种钩子方法的使用。

5.1.3 安卓sdk里的模板方法示例

//钩子方法
if (isShowingLayoutBounds()) {
   debugDrawFocus(canvas);
}
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    /**
     * Manually render this view (and all of its children) to the given Canvas.
     * The view must have already done a full layout before this function is
     * called.  When implementing a view, implement
     * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
     * If you do need to override this method, call the superclass version.
     *
     * @param canvas The Canvas to which the View is rendered.
     */
    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         *      7. If necessary, draw the default focus highlight
         */

        // Step 1, draw the background, if needed
        int saveCount;

        drawBackground(canvas);

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content 空方法,让子类覆盖
            onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);//空方法,子类重写

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // Step 7, draw the default focus highlight
            drawDefaultFocusHighlight(canvas);

            if (isShowingLayoutBounds()) {
                debugDrawFocus(canvas);
            }

            // we're done...
            return;
        }

        /*
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // Step 2, save the canvas' layers
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();
        int topSaveCount = -1;
        int bottomSaveCount = -1;
        int leftSaveCount = -1;
        int rightSaveCount = -1;

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            if (drawTop) {
                topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);
            }

            if (drawBottom) {
                bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
            }

            if (drawLeft) {
                leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);
            }

            if (drawRight) {
                rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // Step 5, draw the fade effect and restore layers
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        // must be restored in the reverse order that they were saved
        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            if (solidColor == 0) {
                canvas.restoreUnclippedLayer(rightSaveCount, p);

            } else {
                canvas.drawRect(right - length, top, right, bottom, p);
            }
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            if (solidColor == 0) {
                canvas.restoreUnclippedLayer(leftSaveCount, p);
            } else {
                canvas.drawRect(left, top, left + length, bottom, p);
            }
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            if (solidColor == 0) {
                canvas.restoreUnclippedLayer(bottomSaveCount, p);
            } else {
                canvas.drawRect(left, bottom - length, right, bottom, p);
            }
        }

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            if (solidColor == 0) {
                canvas.restoreUnclippedLayer(topSaveCount, p);
            } else {
                canvas.drawRect(left, top, right, top + length, p);
            }
        }

        canvas.restoreToCount(saveCount);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        drawDefaultFocusHighlight(canvas);

        if (isShowingLayoutBounds()) {
            debugDrawFocus(canvas);
        }
    }
            
            
    /**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
    }
            
            
    /**
     * Called by draw to draw the child views. This may be overridden
     * by derived classes to gain control just before its children are drawn
     * (but after its own view has been drawn).
     * @param canvas the canvas on which to draw the view
     */
    protected void dispatchDraw(Canvas canvas) {

    }
}

5.2 命令模式 Command Pattern

5.2.1 基本介绍

**意图:**将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

**主要解决:**在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

**何时使用:**在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

**如何解决:**通过调用者调用接受者执行命令,顺序:调用者→命令→接受者

**关键代码:**定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口

**应用实例:**struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。

优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

**缺点:**使用命令模式可能会导致某些系统有过多的具体命令类。

**使用场景:**认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。

**注意事项:**系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

  1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。

  2. 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令

  3. 容易实现对请求的撤销和重做

  4. 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意

  5. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。

  6. 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令) 订单的撤销/恢复、触发-反馈机制

命令模式相当角色

调用者:Client

命令:Commond

命令执行者:Receiver

他们之间的关系:命令组合执行者,命令本身没有执行逻辑,只有命令相关信息,执行者根据信息执行相应的逻辑,调用者只发命令即可,调用者不需要关心命令的执行者是谁。调用者本身有记录当前正在执行的命令,有命令队列,发送命令,撤消命令等逻辑,

5.2.2 代码示例

//命令,只与命令执行者打交道
interface ICommand {
    fun execute(): String
}


class WindowCommand(val cmd: String, val receiver: IReceiver) : ICommand {

    override fun execute(): String {
        return receiver.handleCommand(cmd).also {
            println(it)
        }
    }
}


//命令接收/执行得,只与command打交道
class WindowReceiver : IReceiver {

    companion object {
        private val LINE_SEP = System.getProperty("line.separator")
    }

    private fun getStreamInfo(inputStream:InputStream):String {
        val msg = StringBuilder()
        BufferedReader(InputStreamReader(inputStream))
            .use { bufferedReader ->
                while (true) {
                    val line = bufferedReader.readLine()
                    if(line!=null && line.isNotEmpty()) {
                        msg.append(line).append("\n\r")
                    } else {
                        break
                    }
                }
            }
        return msg.toString()
    }

    override fun handleCommand(cmd: String): String {
        val process = Runtime.getRuntime().exec(cmd)

        val successMsg = getStreamInfo(process.inputStream)
        if(successMsg.isNotEmpty()) return successMsg

        return getStreamInfo(process.errorStream)
    }
}


//命令调用者
//调用者只与ICommand打交道
class Invoker {

    private val gitCommand = WindowCommand("git",WindowReceiver())
    private val adbCommand = WindowCommand("adb",WindowReceiver())

    private val commandMap = mapOf<String,ICommand>().apply {
        "git" to gitCommand
        "adb" to adbCommand
    }

    private val commands = mutableListOf<ICommand>()

    private var curCommand: ICommand? = null

    private var isStop = false
    private var thread: Thread? = null

    fun addCommand(command: ICommand) {
        commands.add(command)
    }

    fun printAllCommand() {
        println(commandMap)
    }

    //执行内置好的命令 adb
    fun executeAdb() {
        adbCommand.execute()
    }

    //执行内置好的命令 git
    fun executeGit() {
        gitCommand.execute()
    }

    fun cancel() {
        println("取消运行任务队列")
        isStop = true
        commands.clear()
    }

    fun stop() {
        isStop = true
        println("暂停任务队列执行")
    }

    fun resume() {
        println("重新运行任务队列")
        isStop = false
        execute()
    }

    fun execute() {
        Thread {
            while (!isStop && commands.isNotEmpty()) {
                curCommand = commands.removeFirst()
                println("队列还有${commands.size}个任务")
                curCommand?.execute()
            }
        }.also {
            thread = it
        }.start()
    }

}


fun main() {

    //直接执行一个命令
    WindowCommand("git", WindowReceiver()).execute()

    //添加
    val invoker = Invoker().apply {
        //执行内置好的命令
        this.executeAdb()
        (0..100).forEach {
            addCommand(WindowCommand("adb devices", WindowReceiver()))
        }
        execute()
    }

    val thread = Thread {
        Thread.sleep(2000)
        invoker.stop()
        Thread.sleep(2000)
        invoker.resume()
        Thread.sleep(2000)
        invoker.cancel()
    }.apply {
        start()
    }

    thread.join()
}

5.2.3 命令模式在安卓Sdk里的示例

回到Android消息机制,上边说到Android消息机制是一个非典型的命令模式,其分工如下: 接收者:Handler,执行消息处理操作; 调用者:Looper,调用消息的的处理方法; 命令角色:Message,消息类。

5.3 访问者模式 Visitor Pattern

5.3.1 访问者模式基本介绍

**意图:**主要将数据结构与数据操作分离。

**主要解决:**稳定的数据结构和易变的操作耦合问题。

**何时使用:**需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。

**如何解决:**在被访问的类里面加一个对外提供接待访问者的接口。

**关键代码:**在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

**应用实例:**您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。

优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。

缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。

**注意事项:**访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

5.3.2 访问者模式的角色及职责

  1. Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作

  2. ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.

  3. ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素

  4. Element 定义一个accept 方法,接收一个访问者对象

  5. ConcreteElement 为具体元素,实现了accept 方法

-上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。

双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型

  • 以上述实例为例,假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,**只需增加一个Action **子类即可在客户端调用即可,不需要改动任何其他类的代码。
//代表数据结构
public abstract class Person {
   
   //提供一个方法,让访问者可以访问
   public abstract void accept(Action action);
}

public class Man extends Person {
   @Override
   public void accept(Action action) {
      // TODO Auto-generated method stub
      action.getManResult(this);
   }
}

public class Woman extends Person{
   @Override
   public void accept(Action action) {
      // TODO Auto-generated method stub
      action.getWomanResult(this);
   }
}

//代表操作
public abstract class Action {
   
   //得到男性 的测评
   public abstract void getManResult(Man man);
   
   //得到女的 测评
   public abstract void getWomanResult(Woman woman);
}

public class Fail extends Action {

   @Override
   public void getManResult(Man man) {
      // TODO Auto-generated method stub
      System.out.println(" 男人给的评价该歌手失败 !");
   }

   @Override
   public void getWomanResult(Woman woman) {
      // TODO Auto-generated method stub
      System.out.println(" 女人给的评价该歌手失败 !");
   }
}

public class Success extends Action {  @Override
   public void getManResult(Man man) {
      // TODO Auto-generated method stub
      System.out.println(" 男人给的评价该歌手很成功 !");
   }

   @Override
   public void getWomanResult(Woman woman) {
      // TODO Auto-generated method stub
      System.out.println(" 女人给的评价该歌手很成功 !");
   }
}

public class Wait extends Action {

   @Override
   public void getManResult(Man man) {
      // TODO Auto-generated method stub
      System.out.println(" 男人给的评价是该歌手待定 ..");
   }

   @Override
   public void getWomanResult(Woman woman) {
      // TODO Auto-generated method stub
      System.out.println(" 女人给的评价是该歌手待定 ..");
   }
}

//数据结构,管理很多人(Man , Woman)
public class ObjectStructure {

   //维护了一个集合
   private List<Person> persons = new LinkedList<>();
   
   //增加到list
   public void attach(Person p) {
      persons.add(p);
   }
   //移除
   public void detach(Person p) {
      persons.remove(p);
   }
   
   //显示测评情况
   public void display(Action action) {
      for(Person p: persons) {
         p.accept(action);
      }
   }
}

public class Client {

   public static void main(String[] args) {
      // TODO Auto-generated method stub
      //创建ObjectStructure
      ObjectStructure objectStructure = new ObjectStructure();
      
      objectStructure.attach(new Man());
      objectStructure.attach(new Woman());
      
      
      //成功
      Success success = new Success();
      objectStructure.display(success);
      
      System.out.println("===============");
      Fail fail = new Fail();
      objectStructure.display(fail);
      
      System.out.println("=======给的是待定的测评========");
      
      Wait wait = new Wait();
      objectStructure.display(wait);
   }
}

5.4 迭代器模式 Iterator Pattern

5.4.1基本介绍

**意图:**提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

**主要解决:**不同的方式来遍历整个整合对象。

**何时使用:**遍历一个聚合对象。

**如何解决:**把在元素之间游走的责任交给迭代器,而不是聚合对象。

**关键代码:**定义接口:hasNext, next。

**应用实例:**JAVA 中的 iterator。

优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

**缺点:**由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。

**注意事项:**迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

//代表需要遍历的基类,使用通用的Iterator迭代器来遍历不同的数据结构。
public interface College {
   
   public String getName();
   
   //增加系的方法
   public void addDepartment(String name, String desc);
   
   //返回一个迭代器,遍历
   public Iterator  createIterator();
}

//使用Iterator遍历数组[]
public class ComputerCollege implements College {

   Department[] departments;
   int numOfDepartment = 0 ;// 保存当前数组的对象个数
   
   
   public ComputerCollege() {
      departments = new Department[5];
      addDepartment("Java专业", " Java专业 ");
      addDepartment("PHP专业", " PHP专业 ");
      addDepartment("大数据专业", " 大数据专业 ");
      
   }
   
   
   @Override
   public String getName() {
      // TODO Auto-generated method stub
      return "计算机学院";
   }

   @Override
   public void addDepartment(String name, String desc) {
      // TODO Auto-generated method stub
      Department department = new Department(name, desc);
      departments[numOfDepartment] = department;
      numOfDepartment += 1;
   }

   @Override
   public Iterator createIterator() {
      // TODO Auto-generated method stub
      return new ComputerCollegeIterator(departments);
   }

}

//使用Iterator遍历List
public class InfoCollege implements College {

   List<Department> departmentList;
   
   
   public InfoCollege() {
      departmentList = new ArrayList<Department>();
      addDepartment("信息安全专业", " 信息安全专业 ");
      addDepartment("网络安全专业", " 网络安全专业 ");
      addDepartment("服务器安全专业", " 服务器安全专业 ");
   }
   
   @Override
   public String getName() {
      // TODO Auto-generated method stub
      return "信息工程学院";
   }

   @Override
   public void addDepartment(String name, String desc) {
      // TODO Auto-generated method stub
      Department department = new Department(name, desc);
      departmentList.add(department);
   }

   @Override
   public Iterator createIterator() {
      // TODO Auto-generated method stub
      return new InfoColleageIterator(departmentList);
   }
}


public class OutPutImpl {
   
   //学院集合
   List<College> collegeList;

   public OutPutImpl(List<College> collegeList) {
      
      this.collegeList = collegeList;
   }
   //遍历所有学院,然后调用printDepartment 输出各个学院的系
   public void printCollege() {
      
      //从collegeList 取出所有学院, Java 中的 List 已经实现Iterator
      Iterator<College> iterator = collegeList.iterator();
      
      while(iterator.hasNext()) {
         //取出一个学院
         College college = iterator.next();
         System.out.println("=== "+college.getName() +"=====" );
         printDepartment(college.createIterator()); //得到对应迭代器
      }
   }
   
   
   //输出 学院输出 系
   
   public void printDepartment(Iterator iterator) {
      while(iterator.hasNext()) {
         Department d = (Department)iterator.next();
         System.out.println(d.getName());
      }
   }
}


public class Client {

   public static void main(String[] args) {
      // TODO Auto-generated method stub
      //创建学院
      List<College> collegeList = new ArrayList<College>();
      
      ComputerCollege computerCollege = new ComputerCollege();
      InfoCollege infoCollege = new InfoCollege();
      
      collegeList.add(computerCollege);
      collegeList.add(infoCollege);
      
      OutPutImpl outPutImpl = new OutPutImpl(collegeList);
      outPutImpl.printCollege();
   }

}

5.5 观察者模式 Observer Pattern

5.5.1 基本介绍

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

**主要解决:**一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

**何时使用:**一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

**如何解决:**使用面向对象技术,可以将这种依赖关系弱化。

**关键代码:**在抽象类里有一个 ArrayList 存放观察者们。

应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

5.5.2 android sdk里观察者模式的示例

public class RecyclerView extends ViewGroup implements ScrollingView,
        NestedScrollingChild2, NestedScrollingChild3 {
            
    //观察者
    private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

            
    
    /**
     * Base class for an Adapter
     *
     * <p>Adapters provide a binding from an app-specific data set to views that are displayed
     * within a {@link RecyclerView}.</p>
     *
     * @param <VH> A class that extends ViewHolder that will be used by the adapter.
     */
    public abstract static class Adapter<VH extends ViewHolder> {
        private final AdapterDataObservable mObservable = new AdapterDataObservable();
        private boolean mHasStableIds = false;
        private StateRestorationPolicy mStateRestorationPolicy = StateRestorationPolicy.ALLOW;

        ......

        public final boolean hasObservers() {
            return mObservable.hasObservers();
        }

        public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
            mObservable.registerObserver(observer);
        }

        public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
            mObservable.unregisterObserver(observer);
        }

        public final void notifyDataSetChanged() {
            mObservable.notifyChanged();
        }

        public final void notifyItemChanged(int position) {
            mObservable.notifyItemRangeChanged(position, 1);
        }
        
        public final void notifyItemChanged(int position, @Nullable Object payload) {
            mObservable.notifyItemRangeChanged(position, 1, payload);
        }

        public final void notifyItemRangeChanged(int positionStart, int itemCount) {
            mObservable.notifyItemRangeChanged(positionStart, itemCount);
        }

      
        public final void notifyItemRangeChanged(int positionStart, int itemCount,
                @Nullable Object payload) {
            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
        }

        public final void notifyItemInserted(int position) {
            mObservable.notifyItemRangeInserted(position, 1);
        }

        public final void notifyItemMoved(int fromPosition, int toPosition) {
            mObservable.notifyItemMoved(fromPosition, toPosition);
        }

        public final void notifyItemRangeInserted(int positionStart, int itemCount) {
            mObservable.notifyItemRangeInserted(positionStart, itemCount);
        }

        public final void notifyItemRemoved(int position) {
            mObservable.notifyItemRangeRemoved(position, 1);
        }

        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
        }

       ......
    }
}

private class RecyclerViewDataObserver extends AdapterDataObserver {
    RecyclerViewDataObserver() {
    }

    @Override
    public void onChanged() {
        assertNotInLayoutOrScroll(null);
        mState.mStructureChanged = true;

        processDataSetCompletelyChanged(true);
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }

    @Override
    public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
        assertNotInLayoutOrScroll(null);
        if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
            triggerUpdateProcessor();
        }
    }

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        assertNotInLayoutOrScroll(null);
        if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
            triggerUpdateProcessor();
        }
    }

    @Override
    public void onItemRangeRemoved(int positionStart, int itemCount) {
        assertNotInLayoutOrScroll(null);
        if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
            triggerUpdateProcessor();
        }
    }

    @Override
    public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
        assertNotInLayoutOrScroll(null);
        if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
            triggerUpdateProcessor();
        }
    }

    void triggerUpdateProcessor() {
        if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
            ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
        } else {
            mAdapterUpdateDuringMeasure = true;
            requestLayout();
        }
    }
}

5.6 中介者模式 Mediator Pattern

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

5.6.1 中介者模式

**意图:**用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

**主要解决:**对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

**何时使用:**多个类相互耦合,形成了网状结构。

**如何解决:**将上述网状结构分离为星型结构。

**关键代码:**对象 Colleague 之间的通信封装到一个类中单独处理。

应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。

优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。

**缺点:**中介者会庞大,变得复杂难以维护。

使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

**注意事项:**不应当在职责混乱的时候使用。

5.6.2 代码示例

//同事抽象类
public abstract class Colleague {
    //子系统组合了中介者
   private Mediator mediator;
   public String name;

   public Colleague(Mediator mediator, String name) {

      this.mediator = mediator;
      this.name = name;

   }

   public Mediator GetMediator() {
      return this.mediator;
   }

   public abstract void SendMessage(int stateChange);
}

//中介者保存着子系统集合
public abstract class Mediator {
   //将给中介者对象,加入到集合中
   public abstract void Register(String colleagueName, Colleague colleague);

   //接收消息, 具体的同事对象发出
   public abstract void GetMessage(int stateChange, String colleagueName);

   public abstract void SendMessage();
}

//具体的同事类
public class Alarm extends Colleague {

   //构造器
   public Alarm(Mediator mediator, String name) {
      super(mediator, name);
      // TODO Auto-generated constructor stub
      //在创建Alarm 同事对象时,将自己放入到ConcreteMediator 对象中[集合]
      mediator.Register(name, this);
   }

   public void SendAlarm(int stateChange) {
      SendMessage(stateChange);
   }

    //子系统自身发送信息,其实是调用了中介者来处理消息。
   @Override
   public void SendMessage(int stateChange) {
      // TODO Auto-generated method stub
      //调用的中介者对象的getMessage
      this.GetMediator().GetMessage(stateChange, this.name);
   }
}


public class TV extends Colleague {

   public TV(Mediator mediator, String name) {
      super(mediator, name);
      // TODO Auto-generated constructor stub
      mediator.Register(name, this);
   }

   @Override
   public void SendMessage(int stateChange) {
      // TODO Auto-generated method stub
      this.GetMediator().GetMessage(stateChange, this.name);
   }

   public void StartTv() {
      // TODO Auto-generated method stub
      System.out.println("It's time to StartTv!");
   }

   public void StopTv() {
      // TODO Auto-generated method stub
      System.out.println("StopTv!");
   }
}

public class ConcreteMediator extends Mediator {
   //集合,放入所有的同事对象
   private HashMap<String, Colleague> colleagueMap;
   private HashMap<String, String> interMap;

   public ConcreteMediator() {
      colleagueMap = new HashMap<String, Colleague>();
      interMap = new HashMap<String, String>();
   }

   @Override
   public void Register(String colleagueName, Colleague colleague) {
      // TODO Auto-generated method stub
      colleagueMap.put(colleagueName, colleague);

      // TODO Auto-generated method stub

      if (colleague instanceof Alarm) {
         interMap.put("Alarm", colleagueName);
      } else if (colleague instanceof CoffeeMachine) {
         interMap.put("CoffeeMachine", colleagueName);
      } else if (colleague instanceof TV) {
         interMap.put("TV", colleagueName);
      } else if (colleague instanceof Curtains) {
         interMap.put("Curtains", colleagueName);
      }

   }

   //具体中介者的核心方法
   //1. 根据得到消息,完成对应任务
   //2. 中介者在这个方法,协调各个具体的同事对象,完成任务
   @Override
   public void GetMessage(int stateChange, String colleagueName) {
      // TODO Auto-generated method stub

      //处理闹钟发出的消息
      if (colleagueMap.get(colleagueName) instanceof Alarm) {
         if (stateChange == 0) {
            ((CoffeeMachine) (colleagueMap.get(interMap
                  .get("CoffeeMachine")))).StartCoffee();
            ((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
         } else if (stateChange == 1) {
            ((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
         }

      } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
         ((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
               .UpCurtains();

      } else if (colleagueMap.get(colleagueName) instanceof TV) {//如果TV发现消息

      } else if (colleagueMap.get(colleagueName) instanceof Curtains) {
         //如果是以窗帘发出的消息,这里处理...
      }

   }

   @Override
   public void SendMessage() {
      // TODO Auto-generated method stub

   }
}


public class ClientTest {

   public static void main(String[] args) {
      //创建一个中介者对象
      Mediator mediator = new ConcreteMediator();
      
      //创建Alarm 并且加入到  ConcreteMediator 对象的HashMap
      Alarm alarm = new Alarm(mediator, "alarm");
      
      //创建了CoffeeMachine 对象,并  且加入到  ConcreteMediator 对象的HashMap
      CoffeeMachine coffeeMachine = new CoffeeMachine(mediator,
            "coffeeMachine");
      
      //创建 Curtains , 并  且加入到  ConcreteMediator 对象的HashMap
      Curtains curtains = new Curtains(mediator, "curtains");
      TV tV = new TV(mediator, "TV");
      
      //让闹钟发出消息
      alarm.SendAlarm(0);
      coffeeMachine.FinishCoffee();
      alarm.SendAlarm(1);
   }
}

5.6.3中介者模式在安卓sdk里的使用示例

public class MediatorLiveData<T> extends MutableLiveData<T> {
    private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();

    /**
     * Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
     * when {@code source} value was changed.
     * <p>
     * {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
     * <p> If the given LiveData is already added as a source but with a different Observer,
     * {@link IllegalArgumentException} will be thrown.
     *
     * @param source    the {@code LiveData} to listen to
     * @param onChanged The observer that will receive the events
     * @param <S>       The type of data hold by {@code source} LiveData
     */
    @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            e.plug();
        }
    }

    /**
     * Stops to listen the given {@code LiveData}.
     *
     * @param toRemote {@code LiveData} to stop to listen
     * @param <S>      the type of data hold by {@code source} LiveData
     */
    @MainThread
    public <S> void removeSource(@NonNull LiveData<S> toRemote) {
        Source<?> source = mSources.remove(toRemote);
        if (source != null) {
            source.unplug();
        }
    }

    @CallSuper
    @Override
    protected void onActive() {
        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
            source.getValue().plug();
        }
    }

    @CallSuper
    @Override
    protected void onInactive() {
        for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
            source.getValue().unplug();
        }
    }

    private static class Source<V> implements Observer<V> {
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;

        Source(LiveData<V> liveData, final Observer<? super V> observer) {
            mLiveData = liveData;
            mObserver = observer;
        }

        void plug() {
            mLiveData.observeForever(this);
        }

        void unplug() {
            mLiveData.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable V v) {
            if (mVersion != mLiveData.getVersion()) {
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            }
        }
    }
}

//MediatorLiveData使用示例
 liveDataMerger.addSource(liveData1, new Observer () {
       private int count = 1;
 
        @Override public void onChanged(@Nullable Integer s) {
           count++;
           liveDataMerger.setValue(s);
           if (count > 10) {
               liveDataMerger.removeSource(liveData1);
           }
       }
  });

liveDataMerger.addSource(liveData2, new Observer () {
       private int count = 1;
 
        @Override public void onChanged(@Nullable Integer s) {
           count++;
           liveDataMerger.setValue(s);
           if (count > 10) {
               liveDataMerger.removeSource(liveData2);
           }
       }
  });


5.7 备忘录模式 Memento Pattern

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。

5.7.1 基本介绍

**意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

**主要解决:**所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

**何时使用:**很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。

**如何解决:**通过一个备忘录类专门存储对象状态。

**关键代码:**客户不与备忘录类耦合,与备忘录管理类耦合。

应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctrl + z。 4、IE 中的后退。 5、数据库的事务管理。

优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。

**缺点:**消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

使用场景: 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。

注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。

//原对象,将有一个状态要被保存。
public class Originator {
   private String state;
 
   public void setState(String state){
      this.state = state;
   }
 
   public String getState(){
      return state;
   }
 
   public Memento saveStateToMemento(){
      return new Memento(state);
   }
 
   public void getStateFromMemento(Memento Memento){
      state = Memento.getState();
   }
}

//备忘录,保存着状态
public class Memento {
   private String state;
 
   public Memento(String state){
      this.state = state;
   }
 
   public String getState(){
      return state;
   }  
}

//管理员
public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();
 
   public void add(Memento state){
      mementoList.add(state);
   }
 
   public Memento get(int index){
      return mementoList.get(index);
   }
}

public class MementoPatternDemo {
   public static void main(String[] args) {
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #3");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #4");
 
      System.out.println("Current State: " + originator.getState());    
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
      originator.getStateFromMemento(careTaker.get(1));
      System.out.println("Second saved State: " + originator.getState());
   }
}

5.7.2 备忘录模式在安卓sdk里的示例

Activity 里的onSaveInstanceStateonRestoreInstanceState 保存的状态是Bundle

@UiContext
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback,
        AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
            

            
    /**
     * Called to retrieve per-instance state from an activity before being killed
     * so that the state can be restored in {@link #onCreate} or
     * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
     * will be passed to both).
     *
     * <p>This method is called before an activity may be killed so that when it
     * comes back some time in the future it can restore its state.  For example,
     * if activity B is launched in front of activity A, and at some point activity
     * A is killed to reclaim resources, activity A will have a chance to save the
     * current state of its user interface via this method so that when the user
     * returns to activity A, the state of the user interface can be restored
     * via {@link #onCreate} or {@link #onRestoreInstanceState}.
     *
     * <p>Do not confuse this method with activity lifecycle callbacks such as {@link #onPause},
     * which is always called when the user no longer actively interacts with an activity, or
     * {@link #onStop} which is called when activity becomes invisible. One example of when
     * {@link #onPause} and {@link #onStop} is called and not this method is when a user navigates
     * back from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
     * on B because that particular instance will never be restored,
     * so the system avoids calling it.  An example when {@link #onPause} is called and
     * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
     * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
     * killed during the lifetime of B since the state of the user interface of
     * A will stay intact.
     *
     * <p>The default implementation takes care of most of the UI per-instance
     * state for you by calling {@link android.view.View#onSaveInstanceState()} on each
     * view in the hierarchy that has an id, and by saving the id of the currently
     * focused view (all of which is restored by the default implementation of
     * {@link #onRestoreInstanceState}).  If you override this method to save additional
     * information not captured by each individual view, you will likely want to
     * call through to the default implementation, otherwise be prepared to save
     * all of the state of each view yourself.
     *
     * <p>If called, this method will occur after {@link #onStop} for applications
     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
     * For applications targeting earlier platform versions this method will occur
     * before {@link #onStop} and there are no guarantees about whether it will
     * occur before or after {@link #onPause}.
     *
     * @param outState Bundle in which to place your saved state.
     *
     * @see #onCreate
     * @see #onRestoreInstanceState
     * @see #onPause
     */
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        dispatchActivitySaveInstanceState(outState);
    }
            
                /**
     * Called to retrieve per-instance state from an activity before being killed
     * so that the state can be restored in {@link #onCreate} or
     * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
     * will be passed to both).
     *
     * <p>This method is called before an activity may be killed so that when it
     * comes back some time in the future it can restore its state.  For example,
     * if activity B is launched in front of activity A, and at some point activity
     * A is killed to reclaim resources, activity A will have a chance to save the
     * current state of its user interface via this method so that when the user
     * returns to activity A, the state of the user interface can be restored
     * via {@link #onCreate} or {@link #onRestoreInstanceState}.
     *
     * <p>Do not confuse this method with activity lifecycle callbacks such as {@link #onPause},
     * which is always called when the user no longer actively interacts with an activity, or
     * {@link #onStop} which is called when activity becomes invisible. One example of when
     * {@link #onPause} and {@link #onStop} is called and not this method is when a user navigates
     * back from activity B to activity A: there is no need to call {@link #onSaveInstanceState}
     * on B because that particular instance will never be restored,
     * so the system avoids calling it.  An example when {@link #onPause} is called and
     * not {@link #onSaveInstanceState} is when activity B is launched in front of activity A:
     * the system may avoid calling {@link #onSaveInstanceState} on activity A if it isn't
     * killed during the lifetime of B since the state of the user interface of
     * A will stay intact.
     *
     * <p>The default implementation takes care of most of the UI per-instance
     * state for you by calling {@link android.view.View#onSaveInstanceState()} on each
     * view in the hierarchy that has an id, and by saving the id of the currently
     * focused view (all of which is restored by the default implementation of
     * {@link #onRestoreInstanceState}).  If you override this method to save additional
     * information not captured by each individual view, you will likely want to
     * call through to the default implementation, otherwise be prepared to save
     * all of the state of each view yourself.
     *
     * <p>If called, this method will occur after {@link #onStop} for applications
     * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
     * For applications targeting earlier platform versions this method will occur
     * before {@link #onStop} and there are no guarantees about whether it will
     * occur before or after {@link #onPause}.
     *
     * @param outState Bundle in which to place your saved state.
     *
     * @see #onCreate
     * @see #onRestoreInstanceState
     * @see #onPause
     */
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        dispatchActivitySaveInstanceState(outState);
    }
            
            
    /**
     * This method is called after {@link #onStart} when the activity is
     * being re-initialized from a previously saved state, given here in
     * <var>savedInstanceState</var>.  Most implementations will simply use {@link #onCreate}
     * to restore their state, but it is sometimes convenient to do it here
     * after all of the initialization has been done or to allow subclasses to
     * decide whether to use your default implementation.  The default
     * implementation of this method performs a restore of any view state that
     * had previously been frozen by {@link #onSaveInstanceState}.
     *
     * <p>This method is called between {@link #onStart} and
     * {@link #onPostCreate}. This method is called only when recreating
     * an activity; the method isn't invoked if {@link #onStart} is called for
     * any other reason.</p>
     *
     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
     *
     * @see #onCreate
     * @see #onPostCreate
     * @see #onResume
     * @see #onSaveInstanceState
     */
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        if (mWindow != null) {
            Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
            if (windowState != null) {
                mWindow.restoreHierarchyState(windowState);
            }
        }
    }

    /**
     * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
     * created with the attribute {@link android.R.attr#persistableMode} set to
     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
     * came from the restored PersistableBundle first
     * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
     *
     * <p>This method is called between {@link #onStart} and
     * {@link #onPostCreate}.
     *
     * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called.
     *
     * <p>At least one of {@code savedInstanceState} or {@code persistentState} will not be null.
     *
     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}
     *     or null.
     * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}
     *     or null.
     *
     * @see #onRestoreInstanceState(Bundle)
     * @see #onCreate
     * @see #onPostCreate
     * @see #onResume
     * @see #onSaveInstanceState
     */
    public void onRestoreInstanceState(@Nullable Bundle savedInstanceState,
            @Nullable PersistableBundle persistentState) {
        if (savedInstanceState != null) {
            onRestoreInstanceState(savedInstanceState);
        }
    }
}

5.8 解释器模式(Interpreter模式)

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

5.8.1基本介绍

**意图:**给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

**主要解决:**对于一些固定文法构建一个解释句子的解释器。

**何时使用:**如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

**如何解决:**构建语法树,定义终结符与非终结符。

**关键代码:**构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。

**应用实例:**编译器、运算表达式计算。

优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。

缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。

使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。

**注意事项:**可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。

5.8.2 代码示例

/**
 * 抽象类表达式,通过HashMap 键值对, 可以获取到变量的值
 * 
 * @author Administrator
 *
 */
public abstract class Expression {
   // a + b - c
   // 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是就是具体值
   // HashMap {a=10, b=20}
   public abstract int interpreter(HashMap<String, Integer> var);
}


/**
 * 变量的解释器
 * @author Administrator
 *
 */
public class VarExpression extends Expression {

   private String key; // key=a,key=b,key=c

   public VarExpression(String key) {
      this.key = key;
   }

   // var 就是{a=10, b=20}
   // interpreter 根据 变量名称,返回对应值
   @Override
   public int interpreter(HashMap<String, Integer> var) {
      return var.get(this.key);
   }
}


/**
 * 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
 * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类
 * 
 * @author Administrator
 *
 */
public class SymbolExpression extends Expression {

   protected Expression left;
   protected Expression right;

   public SymbolExpression(Expression left, Expression right) {
      this.left = left;
      this.right = right;
   }

   //因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现
   @Override
   public int interpreter(HashMap<String, Integer> var) {
      // TODO Auto-generated method stub
      return 0;
   }
}


/**
 * 加法解释器
 * @author Administrator
 *
 */
public class AddExpression extends SymbolExpression  {

   public AddExpression(Expression left, Expression right) {
      super(left, right);
   }

   //处理相加
   //var 仍然是 {a=10,b=20}..
   //一会我们debug 源码,就ok
   public int interpreter(HashMap<String, Integer> var) {
      //super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
      //super.right.interpreter(var): 返回right 表达式对应值 b = 20
      return super.left.interpreter(var) + super.right.interpreter(var);
   }
}



public class SubExpression extends SymbolExpression {

   public SubExpression(Expression left, Expression right) {
      super(left, right);
   }

   //求出left 和 right 表达式相减后的结果
   public int interpreter(HashMap<String, Integer> var) {
      return super.left.interpreter(var) - super.right.interpreter(var);
   }
}


public class Calculator {

   // 定义表达式
   private Expression expression;

   // 构造函数传参,并解析
   public Calculator(String expStr) { // expStr = a+b
      // 安排运算先后顺序
      Stack<Expression> stack = new Stack<>();
      // 表达式拆分成字符数组 
      char[] charArray = expStr.toCharArray();// [a, +, b]

      Expression left = null;
      Expression right = null;
      //遍历我们的字符数组, 即遍历  [a, +, b]
      //针对不同的情况,做处理
      for (int i = 0; i < charArray.length; i++) {
         switch (charArray[i]) {
         case '+': //
            left = stack.pop();// 从stack取出left => "a"
            right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
            stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
            break;
         case '-': // 
            left = stack.pop();
            right = new VarExpression(String.valueOf(charArray[++i]));
            stack.push(new SubExpression(left, right));
            break;
         default: 
            //如果是一个 Var 就创建要给 VarExpression 对象,并push到 stack
            stack.push(new VarExpression(String.valueOf(charArray[i])));
            break;
         }
      }
      //当遍历完整个 charArray 数组后,stack 就得到最后Expression
      this.expression = stack.pop();
   }

   public int run(HashMap<String, Integer> var) {
      //最后将表达式a+b和 var = {a=10,b=20}
      //然后传递给expression的interpreter进行解释执行
      return this.expression.interpreter(var);
   }
}


public class ClientTest {

   public static void main(String[] args) throws IOException {
      // TODO Auto-generated method stub
      String expStr = getExpStr(); // a+b
      HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}
      Calculator calculator = new Calculator(expStr);
      System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
   }

   // 获得表达式
   public static String getExpStr() throws IOException {
      System.out.print("请输入表达式:");
      return (new BufferedReader(new InputStreamReader(System.in))).readLine();
   }

   // 获得值映射
   public static HashMap<String, Integer> getValue(String expStr) throws IOException {
      HashMap<String, Integer> map = new HashMap<>();

      for (char ch : expStr.toCharArray()) {
         if (ch != '+' && ch != '-') {
            if (!map.containsKey(String.valueOf(ch))) {
               System.out.print("请输入" + String.valueOf(ch) + "的值:");
               String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
               map.put(String.valueOf(ch), Integer.valueOf(in));
            }
         }
      }

      return map;
   }
}

5.8.3 解释器模式在jdk里的应用

JDK中的正则表达式:Pattern类

5.9 状态模式 State Pattern

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

5.9.1 基本介绍

**意图:**允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

**主要解决:**对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

**何时使用:**代码中包含大量与对象状态有关的条件语句。

**如何解决:**将各种具体的状态类抽象出来。

**关键代码:**通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。

应用实例: 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。 2、曾侯乙编钟中,'钟是抽象接口','钟A'等是具体状态,'曾侯乙编钟'是具体环境(Context)。

优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。

**注意事项:**在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。

5.9.2 代码示例

//状态接口
public interface State {
   public void doAction(Context context);
}

public class StartState implements State {
 
   public void doAction(Context context) {
      System.out.println("Player is in start state");
      context.setState(this); 
   }
 
   public String toString(){
      return "Start State";
   }
}

public class StopState implements State {
 
   public void doAction(Context context) {
      System.out.println("Player is in stop state");
      context.setState(this); 
   }
 
   public String toString(){
      return "Stop State";
   }
}

public class Context {
   private State state;
 
   public Context(){
      state = null;
   }
 
   public void setState(State state){
      this.state = state;     
   }
 
   public State getState(){
      return state;
   }
}

public class StatePatternDemo {
   public static void main(String[] args) {
      Context context = new Context();
 
      StartState startState = new StartState();
      startState.doAction(context);
 
      System.out.println(context.getState().toString());
 
      StopState stopState = new StopState();
      stopState.doAction(context);
 
      System.out.println(context.getState().toString());
   }
}

状态模式的 UML 图

5.10 策略模式 Strategy Pattern

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

5.10.1基本介绍

**意图:**定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

**主要解决:**在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

**何时使用:**一个系统有许多许多类,而区分它们的只是他们直接的行为。

**如何解决:**将这些算法封装成一个一个的类,任意地替换。

**关键代码:**实现同一个接口。

应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

**注意事项:**如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

5.10.2 策略模式和模板模式的区别

模板模式一般只针对一套算法,注重对同一个算法的不同细节进行抽象提供不同的实现。而策略模式注重多套算法多套实现,在算法中间不应该有交集,因此算法和算法之间一般不会有冗余代码!因为不同算法之间的实现一般不同但相近。

模板模式:抽象模板(抽象方法或者protected方法)和具体模板

策略模式:策略模式则是以接口形式提供抽象接口

5.10.3 代码示例

public interface FlyBehavior { 
   void fly(); // 子类具体实现
}

public class BadFlyBehavior implements FlyBehavior {

   @Override
   public void fly() {
      System.out.println(" 飞翔技术一般 ");
   }
}

public class GoodFlyBehavior implements FlyBehavior {

   @Override
   public void fly() {
      System.out.println(" 飞翔技术高超 ~~~");
   }
}

public class NoFlyBehavior implements FlyBehavior{

   @Override
   public void fly() {
      System.out.println(" 不会飞翔  ");
   }
}


//相当于Context,包含有策略接口,
public abstract class Duck {

   //属性, 策略接口
   FlyBehavior flyBehavior;
   //其它属性<->策略接口
   QuackBehavior quackBehavior;
   
   public Duck() {
   
   }

   public abstract void display();//显示鸭子信息
   
   public void fly() {
      //改进
      if(flyBehavior != null) {
         flyBehavior.fly();
      }
   }

   public void setFlyBehavior(FlyBehavior flyBehavior) {
      this.flyBehavior = flyBehavior;
   }

   public void setQuackBehavior(QuackBehavior quackBehavior) {
      this.quackBehavior = quackBehavior;
   }
}

public class PekingDuck extends Duck {

   //假如北京鸭可以飞翔,但是飞翔技术一般
   public PekingDuck() {
      flyBehavior = new BadFlyBehavior();
   }
   
   @Override
   public void display() {
      System.out.println("~~北京鸭~~~");
   }
}


public class ToyDuck extends Duck{

   public ToyDuck() {
      flyBehavior = new NoFlyBehavior();
   }
   
   @Override
   public void display() {
      System.out.println("玩具鸭");
   }
}


public class WildDuck extends Duck {

   //构造器,传入FlyBehavor 的对象
   public  WildDuck() {
      flyBehavior = new GoodFlyBehavior();
   }

   @Override
   public void display() {
      System.out.println(" 这是野鸭 ");
   }
}

public class Client {

   public static void main(String[] args) {
      WildDuck wildDuck = new WildDuck();
      wildDuck.fly();
      
      ToyDuck toyDuck = new ToyDuck();
      toyDuck.fly();
      
      PekingDuck pekingDuck = new PekingDuck();
      pekingDuck.fly();
      
      //动态改变某个对象的行为, 北京鸭 不能飞
      pekingDuck.setFlyBehavior(new NoFlyBehavior());
      System.out.println("北京鸭的实际飞翔能力");
      pekingDuck.fly();
   }
}

5.10.4 策略模式在android sdk里的使用示例

ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, 0f, 1f);
animator.setInterpolator(new AccelerateInterpolator());  //加速
animator.setInterpolator(new OvershootInterpolator());   //跑过头又返回来

5.11 职责链模式(责任链模式) Chain of Responsibility Pattern

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

5.11.1基本介绍

介绍

**意图:**避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

**主要解决:**职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

**何时使用:**在处理消息的时候以过滤很多道。

**如何解决:**拦截的类都实现统一接口。

**关键代码:**Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例: 1、红楼梦中的"击鼓传花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。

**注意事项:**在 JAVA WEB 中遇到很多应用。

5.11.2 代码示例

public abstract class AbstractLogger {
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;
 
   protected int level;
 
   //责任链中的下一个元素
   protected AbstractLogger nextLogger;
 
   public void setNextLogger(AbstractLogger nextLogger){
      this.nextLogger = nextLogger;
   }
 
   public void logMessage(int level, String message){
      if(this.level <= level){
         write(message);
      }
      if(nextLogger !=null){
         nextLogger.logMessage(level, message);
      }
   }
 
   abstract protected void write(String message);
}


public class ConsoleLogger extends AbstractLogger {
 
   public ConsoleLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Standard Console::Logger: " + message);
   }
}


public class ErrorLogger extends AbstractLogger {
 
   public ErrorLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("Error Console::Logger: " + message);
   }
}

public class FileLogger extends AbstractLogger {
 
   public FileLogger(int level){
      this.level = level;
   }
 
   @Override
   protected void write(String message) {    
      System.out.println("File::Logger: " + message);
   }
}

public class ChainPatternDemo {
   
   private static AbstractLogger getChainOfLoggers(){
 
      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
 
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);
 
      return errorLogger;  
   }
 
   public static void main(String[] args) {
      AbstractLogger loggerChain = getChainOfLoggers();
 
      loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
 
      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is a debug level information.");
 
      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}