设计模式 —— 相见恨晚(2)

开篇请看设计模式 —— 相见恨晚(1),由于全部写在一篇会导致篇幅过长,所以另开一篇继续……

七、模版方法模式##

1、定义####

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

看到这个模式的定义就感觉特别亲切,它的使用场景无处不在。用四个字概括就是流程封装。也就是把某个固定的流程封装到一个final函数中,并且让子类能够定制这个流程中的某些甚至所有的步骤。这就要求父类提取提取共用的代码,提升代码的复用率。

2、代码示例##

模版方法模式包含如下三个角色:

  • AbsTemplate:抽象类,定义了一套算法框架
  • ConcreteImplA:具体实现类A
  • ConcreteImplB:具体实现类B
设计模式 —— 相见恨晚(2)_第1张图片

下面写一个示例代码,示例取自《Head First设计模式》:

public abstract class Beverage{
    //泡茶或者咖啡的流程,流程一样,只是其中的具体步骤会有差异
    final void prepareRecip(){
        boilWater();    //第一步烧水,泡茶和泡咖啡该方法是一样的
        brew();         //第二步浸泡茶或咖啡,这一步二者有区别,需要在子类重写
        poriInCup();    //第三步将泡好的茶或咖啡倒入杯子中,这一步相同
        addCondiments();    //最后一步加调味品,这一步有区别
    }
    //下面两个方法,因为泡茶和泡咖啡的操作不同,所以抽象出来,交给子类实现
    abstract void brew();

    abstract void addCondiments();

    void boilWater(){
        doSomething();
    }

    void pourInWater(){
        doSomething();
    }
}

//泡茶的类
public class Tea extends Beverage{

    @Override
    void brew() {
        doSomething();  //执行泡茶的方法
    }

    @Override
    void addCondiments() {
        doSomething();  //执行给茶加调味品的方法
    }
}

//泡咖啡的类
public class Coffee extends Beverage{

    @Override
    void brew() {
        doSomething();  //执行泡咖啡的方法
    }

    @Override
    void addCondiments() {
        doSomething();  //执行给咖啡加调味品的方法
    }
}

八、迭代器模式##

1、定义###

提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露暴露该对象的内部表示。

根据定义,该模式的应用场景也就是用在遍历一个容器的对象时。例如遍历java中的各种集合类,如List、Map等。Android的源码中也为我们提供了迭代器遍历数据,最为典型的例子就是数据库查询使用Cursor,当我们使用SQLiteDatabase的query()方法查询数据时,会返回一个Cursor游标对象,该游标对象实质就是一个具体的迭代器,我们可以使用它来遍历数据库查询所得的结果集。

2、代码示例###

前面的几种设计模式都提供了代码示例,不过迭代器这个模式对开发者来说几乎不会自己去实现一个迭代器,面试时面试官应该也不会让你写个迭代器模式吧,所以这里就不再给出代码示例,不过UML图还是要有的,如下:

设计模式 —— 相见恨晚(2)_第2张图片

九、策略模式##

1、定义###

策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以互相替换。此模式让算法的变化独立于使用算法的客户。

这是该模式的官方定义,但说实话,这个定义不像其它的一些模式,有些一看就明白是怎么回事,但这个不太好懂,下面我用大白话一解释就明白了是怎么回事,其实很简答,只不过用一句话解释不清,需要啰嗦一点:

完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。

例如:有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。

为了解决这些问题,可以定义一些独立的类来封装不同的算法,每一个类封装一个具体的算法,在这里,每一个封装算法的类我们都可以称之为策略(Strategy),为了保证这些策略的一致性,一般会用一个抽象的策略类来做算法的定义,而具体每种算法则对应于一个具体策略类。

2、代码示例###

策略模式中包含如下角色:

  • Strategy:策略的抽象
  • ConcreteStrategy:具体的策略实现
  • Context:用来操作策略的上下文环境
设计模式 —— 相见恨晚(2)_第3张图片

下面以乘坐交通工具的场景为例来写代码:我们出门选择乘坐不同各种交通工具可以作为一种策略,例如可以乘出租或者公交车,每一种交通都有不同的计费方式,选择策略不同,计费方式自然不同。我们就以此为例,看如何运用策略模式来实现:选择不同的交通工具,实现各自的计费。

//计算接口
public interface CalculateStrategy{
    
    int calculatePrice(int km);
    
}

//公交车价格计算策略
public class BusStrategy implements CalculateStrategy{

    @Override
    public int calculatePrice(int km) {
        doSomething();  //公交车的计算方式
    }
}

//出租车价格计算策略
public class TaxiStrategy implements CalculateStrategy{

    @Override
    public int calculatePrice(int km) {
        doSomething();  //出租车的计算方式
    }
}

//创建Context角色,出行费用计算器
public class TranficCalculator{
    CalculateStrategy strategy;
    
    public void setStrategy(CalculateStrategy strategy){
        this.strategy = strategy;
    }
    
    public int calculateStrategy(int km){
        return strategy.calculatePrice(km);
    }
    
    public static void main(String[] args){
        TranficCalculator tranficCalculate = new TranficCalculator();
        tranficCalculate.setStrategy(new BusStrategy());
        System.out.print("公交车行驶10km的计费结果为:" + tranficCalculate.calculateStrategy(10));
    }
}

十、状态模式##

1、定义###

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

状态模式通常会拿来和策略模式比较,因为它俩的结构几乎完全一样,UML图也完全一样,区别在于它俩的“意图”。

刚开始我在学状态模式的时候,也是拿它和策略模式作比较,后来发现完全没必要这样。它俩完全是为了适应不同的运用场景,设计意图也不同,只是最后发现它俩的模式结构基本一样,除此之外没有任何关系。所以在学状态模式的时候,个人认为无需结合策略模式,理解各自的运用场景就好,下面就说说状态模式的使用场景:

在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的对象。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。

如果我们按照常规思路来做的话,代码中必定包含大量的 if-else 语句进行状态的判断,当有新的状态时,就得在类中加入新的 if-else ,这必定会导致这个类变得臃肿,而且不好维护,那这时就可以使用状态模式了。

状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象不依赖于其它的对象而独立变化,这样就通过多态来去除过多的、重复的 if-else 等分支。

如果还不理解,看完下面的例子就应该明白了。

2、代码示例###

状态模式中包含的角色:

  • Strategy:抽象状态类或接口
  • ConcreteStrategy:具体状态类
  • Context:用来操作状态的上下文环境
设计模式 —— 相见恨晚(2)_第4张图片

下面就以电视遥控器为例来演示状态模式的实现。我们知道电视有关机和开机的状态,在开机状态下,可以切换频道、调整音量、关机等,而在关机状态下,这些操作都是无效的,只能执行开机的操作。下面我们就来实现:

//定义状态接口(谁的状态?当然是电视机,遥控器可没有状态)
public interface TvState{
    
    public void Channel();  //切换频道
    public void volum();    //调整音量
    
}

//开机状态下的操作
public class PowerOnState implements TvState{

    @Override
    public void Channel() {
        doSomething();
    }

    @Override
    public void volum() {
        doSomething();

    }
}

//关机状态下的操作
public class PowerOnState implements TvState{
    @Override
    public void Channel() {
        doNothing();
    }

    @Override
    public void volum() {
        doNothing();
    }
}

public class TvController{
    TvState tvState;
    
    public void setTvState(TvState tvState){
        this.tvState = tvState;
    }
    
    public void powerOn(){
        setTvState(new PowerOnState());
        System.out.print("开机啦");
    }
    
    public void powerOff(){
        setTvState(new PowerOffState());
        System.out.print("关机啦");
    }
    
    public void channel(){
        tvState.channel();
    }
    
    public void volum(){
        tvState.volum();
    }
}

//客户端测试类
public class Client{
    public static void main(String[] args){
        TvController tvController = new TvController();
        
        //设置开机状态
        tvController.powerOn();
        tvController.channel();//切换频道操作
        
        //设置关机状态
        tvController.powerOff();
        tvController.channel();//此时切换频道不会生效
    }
}

十一、代理模式##

1、定义###

为其他对象提供一个代理(类)以控制对这个对象的访问。

定义很简短也很好理解,一看就明白,没有什么需要解释的。

2、代码示例###

代理模式包含如下四个角色:

  • Subject:抽象主题类
  • RealSubject:真实主题类
  • ProxySubject:代理类
  • Client:客户类
设计模式 —— 相见恨晚(2)_第5张图片

未完……

你可能感兴趣的:(设计模式 —— 相见恨晚(2))