软件构造学习(五):七种设计模式总结
本篇博客面向软件构造考试进行总结,简单阐述七种设计模式的思想,使用场景,并举一些例子来说明。设计模式主要分三种,分别为创建型模式、结构型模式和行为类模式,这三种又可以详细分为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为参数,实现一种相互委托。
 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号