设计模式-模板方法模式 策略模式

模板方法模式定义

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

  模板方法非常常见,对创建框架来说,由框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。


举个咖啡和茶的例子

用模版方法实现代码复用,让茶,咖啡等一类制作方法类似的饮料借用一个算法框架来实现


茶的制作方法:1.把水煮沸 2.用沸水浸泡茶叶 3.把茶倒进杯子4.加柠檬

咖啡的制作方法1.把水煮沸 2.用沸水冲泡咖啡 3.把咖啡倒进杯子4.加牛奶或者糖

可以看出两者的制作过程其实是采用了相同的算法:

1.把水煮沸 

2.用沸水冲泡咖啡或者茶

3.把饮料倒进杯子

4.加入调料

我们把这个咖啡因类饮料的制作方法抽象出来 实现一个模板方法,模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供实现

public abstract class CaffeineBeverage {
 
//prepareRecipe是我们的模板方法
	final void prepareRecipe() {
		boilWater();   //煮水
		brew();      //冲泡
		pourInCup();   //倒进杯子
		addCondiments();  ///加调料
	}
 
	abstract void brew();  
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
  
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
}
接下来实现咖啡和茶的两个具体子类
public class Tea extends CaffeineBeverage {
	public void brew() {
		System.out.println("Steeping the tea");
	}
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
}
public class Coffee extends CaffeineBeverage {
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
}
对模板方法进行挂钩

  钩子(hook)是一种被声明在抽象类中的方法,但只有空的或者默认的实现钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。

  某些步骤是可选的,所以可以将这些步骤实现成钩子,而不是实现成抽象方法,这样就可以让抽象类的子类的负荷减轻。

 比如,也可以利用钩子做条件控制,影响抽象类中的算法流程:钩子方法在抽象类中有默认实现返回true,放在抽象类的if条件语句中,子类可以覆盖也可以不覆盖这个钩子方法


上述例子可以对ddCondiments()方法进行挂钩,让子类自由选择要不要加调料,抽象类重写如下:

public abstract class CaffeineBeverageWithHook {
 
	void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		if (customerWantsCondiments()) {
			addCondiments();
		}
	}
	abstract void brew(); 
	abstract void addCondiments();
	void boilWater() {
		System.out.println("Boiling water");
	}
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
	boolean customerWantsCondiments() {
		return true;
	}
}
子类需要实现customerWantsCondiments方法,进行判断是否要进行加调料的操作。而实现这个方法要根据顾客的需求来定,getUserInput()方法得到顾客输入的需求信息

//咖啡子类 挂钩版
import java.io.*;

public class CoffeeWithHook extends CaffeineBeverageWithHook {
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
	private String getUserInput() {
		String answer = null;

		System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}
茶的子类挂钩
import java.io.*;

public class TeaWithHook extends CaffeineBeverageWithHook {
	public void brew() {
		System.out.println("Steeping the tea");
	}
 
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
  //对判断条件的实现 如果输入y就加入调料
	public boolean customerWantsCondiments() {
		String answer = getUserInput();
		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
	private String getUserInput() {
		// get the user's response
		String answer = null;

		System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}
//测试
public class BeverageTestDrive {
	public static void main(String[] args) {
 
		Tea tea = new Tea();
		Coffee coffee = new Coffee();
 
		System.out.println("\nMaking tea...");
		tea.prepareRecipe();
 
		System.out.println("\nMaking coffee...");
		coffee.prepareRecipe();

 
		TeaWithHook teaHook = new TeaWithHook();
		CoffeeWithHook coffeeHook = new CoffeeWithHook();
 
		System.out.println("\nMaking tea...");
		teaHook.prepareRecipe();
 
		System.out.println("\nMaking coffee...");
		coffeeHook.prepareRecipe();
	}

模版方法模式是一个很常见的模式,这是因为对创建框架来说,这个模式很方便可以做到有框架控制如何做事情,而由你指定框架中每个步骤的细节,在实际应用中模版方法有很多实现方法。

下面的例子是用模版方法进行排序

class Student implements Comparable<Student>{
    private String name;
    private int age;
    private float score;
    
    public Student(String name, int age, float score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    
    public String toString()
    {
        return name+"\t\t"+age+"\t\t"+score;
    }

    @Override
    public int compareTo(Student o) {
     
        if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部
            return -1;//由高到底排序
        else if(this.score<o.score)
            return 1;
        else{
            if(this.age>o.age)
                return 1;//由底到高排序
            else if(this.age<o.age)
                return -1;
            else
                return 0;
        }
    }
}

public class ComparableDemo01 {

    public static void main(String[] args) {
  
        Student stu[]={new Student("zhangsan",20,90.0f),
                new Student("lisi",22,90.0f),
                new Student("wangwu",20,99.0f),
                new Student("sunliu",22,100.0f)};
        Arrays.sort(stu);
        for(Student s:stu)
        {
            System.out.println(s);
        }
    }
}
排序的实现看起来更像是策略模式,因为策略模式使用对象组合,但是在策略模式中,你所组合的类实现了整个算法。数 组所实现的排序算法并不完整,它需要一个类填补 compareTo()方法的实现。因此认为这更像模版方法

策略模式

 定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

      这个原则的思考方式是,把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分

下面就以一个示意性的实现讲解策略模式实例的结构。


这个模式涉及到三个角色:

  ●  环境(Context)角色:持有一个Strategy的引用。

  ●  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

  ●  具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

策略模式网上的例子有很多,也比较简单,与上面一个排序做对比,下面用comparator实现的排序用的就是策略模式

import java.util.Comparator;

class Student {
    private String name;
    private int age;
    private float score;
    
    public Student(String name, int age, float score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public float getScore() {
        return score;
    }
    public void setScore(float score) {
        this.score = score;
    }

    public String toString()
    {
        return name+"\t\t"+age+"\t\t"+score;
    }

}
class StudentComparator implements Comparator<Student>{

    public int compare(Student o1, Student o2) {
        if(o1.getScore()>o2.getScore())
            return -1;
        else if(o1.getScore()<o2.getScore())
            return 1;
        else{
            if(o1.getAge()>o2.getAge())
                return 1;
            else if(o1.getAge()<o2.getAge())
                return -1;
            else 
                return 0;
        }
    }
    
}
public class comparatorDemo1 {

    public static void main(String[] args) {

        Student stu[]={new Student("zhangsan",20,90.0f),
                new Student("lisi",22,90.0f),
                new Student("wangwu",20,99.0f),
                new Student("sunliu",22,100.0f)};
        java.util.Arrays.sort(stu,new StudentComparator());
        for(Student s:stu)
        {
            System.out.println(s);
        }
    }
}
<span style="font-size:18px;">StudentComparator类完整地实现了整个排序应用的算法,所以它是策略模式</span>

策略模式和模版方法模式对比

1.两者的意图不同。策略模式的意图是定义一个算法家族,把每个算法封装起来并让这些算法可以互换,这样客户可以轻易使用不同的算法。模版方法模式的意图是在抽象类定义一个算法大纲,而由子类定义其中某些步骤的内容,算法的个别步骤可以有不同的实现细节而算法的结果始终维持不变,达到代码的复用效果。


2.实现方法不同。策略模式是通过对象组合的方式实现的,而模版方法模式是通过继承实现的


3.各种的优势不同。模版方法对算法有更多的控制权,它的类的效率会比策略高效,会重复使用到的代码,都被放进超类,好让所有的子类共享,这使得它经常被用于创建框架。策略模式使用对象组合,因此更有弹性,可以方便选择使用不同的算法,加入新的算法也很方便,不需要依赖超类中方法的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值