【哈工大软件构造】七大设计模式之行为类模式

策略模式

产生原因及其解决方案

原因:有多种不同算法来解决同一个任务,但客户端需要动态切换算法,而不是写死在代码里。

解决方案:为不同的实现算法构造抽象接口,运行时动态传入客户端倾向的算法类实例。

优点:对新的算法实现易于拓展,将算法与客户端实现分离。

实例

我们定义一个环境角色类:

public class Context {
    //持有一个具体策略的对象
    private Strategy strategy;
    /**
     * 构造函数,传入一个具体策略对象
     * @param strategy    具体策略对象
     */
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    /**
     * 策略方法
     */
    public void contextInterface(){
        strategy.strategyInterface();
    }
    
}

抽象策略类:

public interface Strategy {
    /**
     * 策略方法
     */
    public void strategyInterface();
}

具体策略类:

public class ConcreteStrategyA implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}


public class ConcreteStrategyB implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}

public class ConcreteStrategyC implements Strategy {

    @Override
    public void strategyInterface() {
        //相关的业务
    }

}

客户端:

public class Client {

    public static void main(String[] args) {
        //选择并创建需要使用的策略对象
        Strategy strategy = new ConcreteStrategyA ();
        //创建环境
        Context context = new Context(strategy);
        double quote = context .strategyInterface(300);
        System.out.println("图书的最终价格为:" + quote);
    }

}

缺点:

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  2. 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。

模板模式

产生原因及其解决方案

原因:几个客户端共享相同的算法,但在具体细节上有所不同,即一个算法由可定制的部分和不变的部分组成。公共步骤不应该在子类中重复,但需要重复使用。(做事情的步骤一样,但具体方法不同)。

解决方案:共性的步骤在抽象类里公共实现,差异化的步骤在各个子类中实现。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。

实例

定义抽象模板类:

public abstract class CarBuilder {
    protected abstract void BuildSkeleton();
    protected abstract void InstallEngine();
    protected abstract void InstallDoor();
    // Template Method that specifies the general logic
    public void BuildCar() { //通用逻辑
        BuildSkeleton();
        InstallEngine();
        InstallDoor();
    } 
}

定义具体模板子类:

public class PorcheBuilder extends CarBuilder {
    protected void BuildSkeleton() {
        System.out.println("Building Porche Skeleton");
    }
    protected void InstallEngine() {
        System.out.println("Installing Porche Engine");
    }
    protected void InstallDoor() {
        System.out.println("Installing Porche Door");
    } 
}
public class BeetleBuilder extends CarBuilder {
    protected void BuildSkeleton() {
        System.out.println("Building Beetle Skeleton");
    }
    protected void InstallEngine() {
        System.out.println("Installing Beetle Engine");
    }
    protected void InstallDoor() {
        System.out.println("Installing Beetle Door");
    } 
}

客户端调用:

public static void main(String[] args) {
    CarBuilder c = new PorcheBuilder();
    c.BuildCar();
    c = new BeetleBuilder();
    c.BuildCar();
}

迭代器模式

产生原因及其解决方案

原因:客户端希望对放入容器/集合类的一组ADT对象进行遍历访问,而无需关心容器的具体类型(也就是说,不管对象被放在哪里,都应该提供同样的访问模式)。

解决方案:使用迭代器模式。

迭代器的结构

迭代器模式主要包含以下角色。

  1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

实现

将自己的集合类实现Iterable接口,并实现自己独特的Iterator迭代器(hasNext,next,remove)。
Iterable接口:
public interface Iterable<T> {
    ...
    Iterator<T> iterator();
}

Iterator接口:

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

实现例子:

public class Pair<E> implements Iterable<E> {
    private final E first, second;
    public Pair(E f, E s) { first = f; second = s; }
    public Iterator<E> iterator() {
        return new PairIterator();
    }
}
private class PairIterator implements Iterator<E> {
    private boolean seenFirst = false, seenSecond = false;
    public boolean hasNext() { return !seenSecond; }
    public E next() {
        if (!seenFirst) { seenFirst = true; return first; }
        if (!seenSecond) { seenSecond = true; return second; }
    throw new NoSuchElementException();
    }
    public void remove() {
        throw new UnsupportedOperationException();
    } 
} 

客户端可以隐式利用这个迭代器进行迭代:

Pair<String> pair = new Pair<String>("foo", "bar");
for (String s : pair) { … }

访问者模式

定义

对于特定类型(object)的特定操作(visit),在运行时将两者动态地绑定在一起,该操作可以灵活更改,无需更改被visit的类。

本质:将数据和作用于数据上的操作分隔开来。

访问者模式的结构

  1. Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。
  2. ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。
  3. Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
  4. ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  5. ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

实现

/* Abstract element interface (visitable) */
public interface ItemElement {
    public int accept(ShoppingCartVisitor visitor);
}
/* Concrete element */
public class Book implements ItemElement{
    private double price;
    ...
    int accept(ShoppingCartVisitor visitor) {
        visitor.visit(this);
    } 
}
public class Fruit implements ItemElement{
    private double weight;
    ...
    int accept(ShoppingCartVisitor visitor) {
        visitor.visit(this);
    }
}

Book通过实现ItemElement接口,声明自己是visitable的,将处理数据的功能委托给外部传入的visitor,以下是visitor的实现:

/* Abstract visitor interface */
public interface ShoppingCartVisitor { 
    int visit(Book book); 
    int visit(Fruit fruit); 
} 
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
    public int visit(Book book) {
        int cost=0;
        if(book.getPrice() > 50){
            cost = book.getPrice()-5;
        }else 
            cost = book.getPrice();
        System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
        return cost;
    }
    public int visit(Fruit fruit) {
        int cost = fruit.getPricePerKg()*fruit.getWeight();
        System.out.println(fruit.getName() + " cost = "+cost);
        return cost;
    } 
}

客户端代码:

public class ShoppingCartClient {
    public static void main(String[] args) {
        ItemElement[] items = new ItemElement[]{
        new Book(20, "1234"),new Book(100, "5678"),
        new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
        int total = calculatePrice(items);
        System.out.println("Total Cost = "+total);
    }
    private static int calculatePrice(ItemElement[] items) {
        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        //只需要更改visitor是具体实现即可改变算法
        int sum=0;
        for(ItemElement item : items)
            sum = sum + item.accept(visitor);
        return sum;
    } 
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值