软件构造学习(五):七种设计模式总结

  本篇博客面向软件构造考试进行总结,简单阐述七种设计模式的思想,使用场景,并举一些例子来说明。设计模式主要分三种,分别为创建型模式、结构型模式和行为类模式,这三种又可以详细分为23种,根据考试要求,本篇对工厂方法模式、适配器模式、装饰器模式、策略模式、模板模式、迭代器模式和访问者模式进行总结。

  一、工厂方法模式

  工厂方法模式是创建型模式的一种,它是指定义一个用于创建对象的接口,将类的实例化推迟到子类。当client不知道创建那个具体类的实例,或者不想再client代码中指明要具体创建的实例时,可以用工厂方法,让其子类来决定实例化哪个类。这种模式一般包括抽象产品类、抽象工厂类 、具体产品类和具体工厂类。

  常规情况:Product p = new ProductTwo();

  工厂方法模式:Product p = new ConcretrTwo.()makeObject();

先看简单工厂模式:

定义一个抽象产品类(可以是一个抽象类,也可以是一个接口,以接口为例):

1 public interface trace{
2     public void setDebug(boolean debug);
3     public void debug(String message);
4     public void error(String message);
5 }

然后是A、B两种具体产品类:

public class FileTrace implements Trace{
    private boolean debug;
    private PrintWriter pw;
    public void setDebug(boolean debug) {
        this.debug = debug;
    }
    public void debug(String message) {
        if(debug) {
            pw.print("Debug:"+message);
            pw.flush();
        }
    }
    public void debug(String message) {
        pw.print("Error:"+message);
        pw.flush();
    }
}
public class SystemTrace implements Trace{
    private boolean debug;
    public void setDebug(boolean debug) {
        this.debug = debug;
    }
    public void debug(String message) {
        if(debug) {
            System.out.println("Debug:"+message);
        }
    }
    public void debug(String message) {
        System.out.println("Error:"+message);
    }
}

然后创建抽象工厂:

interface TraceFactory{
    public Trace getTrace();
    void otherOperation();//也可以实现其他功能
}

具体工厂:

public class Factory implements TraceFactory{
    public Trace getTrace(String type) {
        if(type.equals("file")) {
            return new FileTrace();
        }
        else if(type.equals("system")) {
            return new SystemTrace();
        }
    }
}

那么client在使用工厂方法来创建实例时,得到的实例类型是抽象接口而不是具体类。

Trace log = new Factory().getTrace("flie");
log.setDebug(false);
……

静态工厂方法模式:

  静态工厂方法模式是指在工厂接口中,方法直接设置为静态的,这样可以不用创建实例而直接调用。也就是直接把工厂接口改成如下形式:

public class TraceFactory{
    public static Trace getTrace(String type) {
        if(type.equals("file")) {
            return new FileTrace();
        }
        else if(type.equals("system")) {
            return new SystemTrace();
        }
    }
}

那么client可以直接这样用:

Trace log = TraceFactory.getTrace("system");
log.debug("…");

  总结:

  • 创建抽象产品类或者接口
  • 创建实现类,在实现类中重写抽象类或者接口中的方法
  • 创建工厂类,可以选择简单工厂模式或者静态工厂模式,区别在于是否需要创建实例 

  二、适配器模式

  适配器模式是一种结构模式,它主要是用来消除不匹配造成的类的兼容性问题,可以将某个类/接口转换成client所期望的其他形式。它一般由目标类(Target)、适配器类(Adapter)和适配者类(Adaptee)三部分组成,其中适配者类是一个具体类,包含了客户所希望使用的方法,而适配器类又可以分为类适配器和对象适配器。下面来看一个例子:

目标类:

public interface Target{
    void request();
}

适配者类:

public class Adaptee{
    //所期望使用的方法
    public void adapteeRequest() {
        System.out.println("被适配者");
    }
}

适配器类:

//类适配器:继承
public class Adapter extends Adaptee implements Target{
    @Override
    public void request() {
        super.adapteeRequest();
    }
}
//对象适配器:关联
public class Adapter implements Target{
    private Adaptee adaptee = new Adaptee();//适配者是对象适配器的一个属性
    @Override
    public void request() {
        adaptee.adapteeRequest();
    }
}

  可以看出,无论是继承还是关联,归根结底都是通过委托完成适配,使目标类可以执行适配者类中所期望的方法。

  三、装饰器模式

  装饰器模式能够实现从一个对象的外部来给对象添加功能,实现功能的灵活扩充,对象的动态组合,用每个子类实现不同的特性。对每一个特性构造子类,通过委托的机制增加到对象上。装饰器模式一般包括接口(具体对象要实现的方法)、具体对象(初始化操作)、装饰类(抽象类,初始化具体对象)和具体装饰类。以栈为例,来看代码:

接口:

interface stack{
    void push(Item e);
    Item pop();
}

具体对象(实现最基础的Stack功能):

public class ArrayStack implements Stack{
    public ArrayStack() {……}
    public void push(Item e) {
        …
    }
    public Item pop() {
        …
    }
}

装饰类(抽象类):

public abstract class StackDecorator implements Stack{
    protected final Stack<E> stack;
    public StackDecorator(stack stack) {
        this.stack = stack;
    }
    public void push(Item e) {
        stack.push(e);
    }
    public Item pop() {
        return stack.pop();
    }
    ……
}

具体装饰类:

public class undoStack extends StackDecorator implements stack{
    private final UndoLog log = new Undolog();
    public undoStack(Stack stack) {
        super(stack);
    }
    public void push(Item e) {
        log.append(UndoLog.PUSH,e);//可以增加新的功能
        super.push(e);//基础功能通过委托实现
    }
    //新的功能
    public void undo() {
        ……
    }
}

  如果之后客户端需要一个具有多种特性的Object,可以通过一层一层的装饰来实现:

  Stack s = new ArrayStack();

  Stack t = new undoStack(s);

  Stack a = new SecureStack(t);  

  ……

四、策略模式

  策略模式是一种行为类模式。它适用于对象的某个行为,不同的场景有不同的实现方式的情况,也就是有多种不同的方法来实现同一个任务,需要client根据需要动态地切换,而不是写死在代码中。对于这种需求,传统的解决方案是使用很多的if-else语句,但是这样后期地维护性差,如果新增方法后要再加if,不符合设计模式的开闭原则。而策略模式采用的解决方案是为不同的实现算法创建抽象接口,利用delegation,运行时动态的传入client倾向的算法类实例。

  策略模式一般包括上下文类、抽象策略类(一般是个接口,定义若干算法标识)和具体策略类(重写策略类的方法)。还是来看一个超市普通顾客、会员、超级会员结账的例子:

抽象策略接口:

public interface PayStrategy{
    public double pay(double price);
}

具体策略类:

public class customerStrategy implements PayStrategy{
    @Override
    public double (double price) {
        return price;//普通顾客不打折
    }
}
public class VIPStrategy implements PayStrategy{
    @Override
    public double (double price) {
        return 0.8*price;//会员打八折
    }
}
public class SVIPStrategy implements PayStrategy{
    @Override
    public double (double price) {
        return 0.5*price;//超级会员打五折
    }
}

上下文类(承上启下封装作用):

public class Context() {
    Private PayStrategy strategy;
    public Context(PayStrategy strategy) {
        this.strategy = strategy;
    }
    public double Price(double price) {
        return strategy.Pay(price);
    }
}

  这样,client就可以根据需要自由的选择策略或者添加策略。

 五、模板模式

  模板模式适用于做事的步骤相同,但是具体的方法不同的场景,它将共性的步骤在抽象类内公共实现,将差异化的步骤在各个子类中实现,也就是会定义一个操作中算法的骨架,将一些步骤延迟到子类中。模板模式一般会包括抽象模板类和具体模板类,为了防止恶意操作,一般抽象模板类中的模板方法都会加上final关键字,防止子类覆盖更改流程。对于所有具体类都相同的操作,直接在抽象类中实现,对于各不相同的操作,在抽象类中定义成一个抽象方法,在具体类中再重写。

  比如一个炒菜的模板,模板方法规定炒菜分为倒油、热油、倒入蔬菜、倒入调味料、翻炒五个步骤;其中倒油、热油、翻炒是相同的,可以直接在炒菜的抽象模板类中直接实现,而对于倒入蔬菜和倒入调味料这种有差异化的步骤,就在各个子类中实现。来看看具体代码:

炒菜的抽象模板类:

public abstract class cook{
    //对于都相同的步骤,直接在抽象模板类中实现
    public void pourOil() {
        System.out.println("倒油");
    }
    public void hotOil() {
        System.out.println("热油");
    }
    public void fry() {
        System.out.println("炒啊炒啊炒");
    }
    //差异化的步骤声明成抽象方法
    public abstract void pourVegetable();
    public abstract void pourSauce();
    //模板方法用final声明,防止被子类重写
    public final void CookProcess() {
        this.pourOil();
        this.hotOil();
        this.pourVegetable();
        this.pourSauce();
        this.fry();
    }
}

创建具体模板类:

public class cook_chicken extends cook{
    @Override
    public void pourVegetable() {
        System.out.println("倒入腌好的鸡翅");
    }
    @Override
    public void pourSauce() {
        System.out.println("倒入一瓶可乐,一勺糖,一勺老抽,一勺盐");
    }
}
public class cook_fish extends cook{
    @Override
    public void pourVegetable() {
        System.out.println("倒入鱼肉段");
    }
    @Override
    public void pourSauce() {
        System.out.println("倒入一勺醋,一勺糖,一勺盐,两勺番茄酱");
    }
}

客户端调用:

    cook_chicken chicken = new cook_chicken();
    chicken.CookProcess();
    cook_fish fish = new cook_fish();
    fish.CookProcess();

  这样就实现了共性的步骤在抽象类中公共的实现,特性的步骤延迟到子类中实现。

六、迭代器模式

  迭代器模式是一种非常常用的模式 ,它是用来提供一种方法访问一个容器对象中的各个元素,又无需关心容器的具体类型,不暴露该对象的内部细节。容器和迭代器同生共死!当你自己定义了一种容器,就理应提供对应的迭代器。迭代器模式一般包括抽象容器(一般是一个接口,提供一个iterrator()方法)、具体容器、抽象迭代器和迭代器实现类。

抽象迭代器类(标准的遍历协议):

interface Iterator {
    public Object next();
    public boolean hasNext();
}

迭代器实现类(实现自己独特的迭代器,允许客户端利用这个迭代器进行显示或隐式的迭代遍历):

class ConcreteIterator implements Iterator{
    private List list = new ArrayList();
    private int cursor =0;
    public ConcreteIterator(List list){
        this.list = list;
    }
    public boolean hasNext() {
        if(cursor==list.size()){
            return false;
        }
        return true;
    }
    public Object next() {
        Object obj = null;
        if(this.hasNext()){
            obj = this.list.get(cursor++);
        }
        return obj;
    }
}

抽象容器:

interface Aggregate{
    public void add(Object obj);
    public void remove(Object obj);
    public Iterator iterator();
}

具体容器(你希望能够提供统一遍历机制的ADT):

class ConcreteAggregate implements Aggregate{
    private List list = new ArrayList<>();
    public void add(Object obj) {
        list.add(obj);
    }
    public void remove(Object obj) {
        list.remove(obj);
    }
    public Iterator iterator() {
        return new ConcreteIterator(list);
    }
}

客户端调用:

Aggregate ag = new ConcreteAggregate();
    Iterator it = ag.iterator();
    while(it.hasNext) {
        System.out.println((String)it.next());
    }

 七、访问者模式

  访问者模式是为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下通过delegation接入ADT,本质上是将数据和作用于数据的某些特定的操作分离开来,适用于数据结构相对稳定,但是操作集合可以相对自由地演化的情况。与策略模式相比,两者都是通过delegation建立两个对象的动态联系,但是访问者模式是双向委托,两种对象互为参数。看一个比较简单地例子:

class A{
    public void method1(){
        System.out.println("这是A");
    }
    public void method2(B b){
        b.show(this);
    }
}
class B{
    public void show(A a){
        a.method1;
    }
}

  其实这就是访问者模式的本质,在A中将某个操作委托给作为参数的B,而在B中某个操作又以A为参数,实现一种相互委托。  

posted @ 2022-06-11 16:17  叶绿体基质  阅读(188)  评论(0)    收藏  举报