十:模板方法模式
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
模板方法模式在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
好莱坞原则:高层组件会决定什么时候调用低层组件,但是低层组件不能调用高层组件。
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、Spirng 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
实现:
首先来个模板抽象类:
/*
* 模板方法抽象类
* @author zzf
* @date 2018年10月24日 下午3:56:48
*/
public abstract class Game {
public void initialize() {
System.out.println("初始化游戏……");
}
abstract void startPlay();
abstract void endPlay();
/**
* 钩子
* @return
*/
public boolean isAgain() {
return true;
}
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
if(isAgain())
startPlay();
//结束游戏
endPlay();
}
}
来个飞机实现类:
/*
* @author zzf
* @date 2018年10月24日 下午4:05:15
*/
public class FlyGame extends Game {
@Override
void startPlay() {
// TODO Auto-generated method stub
System.out.println("飞机大战开始……");
}
@Override
void endPlay() {
// TODO Auto-generated method stub
System.out.println("飞机大战结束……");
}
public boolean isAgain() {
try {
if (getUserInput().equals("y")) {
return true;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
private String getUserInput() throws IOException {
String out = null;
System.out.println("输入y(再来一次),输入n(结束):");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
out = in.readLine().equals("y") ? "y" : "n";
return out;
}
}
这里我重新实现了钩子方法并新增了一个获取输入的方法,因为play()方法是模板方法所以不会被继承且不能变。
测试类:
/*
* @author zzf
* @date 2018年10月24日 下午4:15:19
*/
public class DemoGame {
public static void main(String[] args) {
Game flyGame=new FlyGame();
flyGame.play();
}
}
JDK中的排序方法就用到了模板方法模式:
下面简单的来个demo:
/*
* @author zzf
* @date 2018年10月24日 下午4:24:08
*/
public class Duck implements Comparable{
private String name;
private Integer age;
public Duck(String name,Integer age) {
this.name=name;
this.age=age;
}
@Override
public int compareTo(Object o) {
Duck other=(Duck) o;
if(this.age<other.age) {
return -1;
}else if(this.age==other.age) {
return 0;
}else {
return 1;
}
}
@Override
public String toString() {
return "Duck [name=" + name + ", age=" + age + "]";
}
public static void main(String[] args) {
Duck[] ducks= {new Duck("duck1",1)
,new Duck("duck5",5)
,new Duck("duck3",3)
,new Duck("duck2",2)
,new Duck("duck6",6)
,new Duck("duck4",4)
};
System.out.println("排序前:");
display(ducks);
Arrays.sort(ducks);
System.out.println("排序前:");
display(ducks);
}
public static void display(Duck[] ducks) {
for(int i=0;i<ducks.length;i++)
System.out.println(ducks[i]);
}
}
要点:
1、“模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类。
2、模板方法模式为我们提供了一种代码复用的重要技巧。
3、模板方法的抽象类可以定义具体方法、抽象方法和钩子。
4、抽象方法由子类实现。
5、钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择是否要覆盖。
6、为了防止子类改变模板方法中的算法,可以将模板方法声明为final。
7、好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用低层模块。
8、策略模式和模板方法模式都封装了算法,一个用组合,一个用继承。
9、工厂方法是模板方法的一种特殊版本。
10、模板方法模式提供一个大纲具体实现由子类实现,策略模式中的算法组由不同的类进行实现。
十一:迭代器模式
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
实现:
首先新增迭代器接口:
/*
* @author zzf
* @date 2018年10月25日 下午2:30:55
*/
public interface Iterator {
boolean hasNext();
Object next();
}
新增返回迭代器的容器接口:
/*
* @author zzf
* @date 2018年10月25日 下午2:57:17
*/
public interface Container {
public Iterator getIterator();
}
创建一个User类:
/*
* @author zzf
* @date 2018年10月25日 下午4:53:38
*/
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public User(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
创建两个迭代器实现类和容器的实现类:
/*
* @author zzf
* @date 2018年10月25日 下午2:58:21
*/
public class ArrayIterator implements Iterator {
private User[] users;
private int index=0;
public ArrayIterator(User[] user) {
this.users=user;
}
@Override
public boolean hasNext() {
if (index < users.length) {
return true;
}
return false;
}
@Override
public Object next() {
if (this.hasNext()) {
return users[index++];
}
return null;
}
}
/*
* @author zzf
* @date 2018年10月25日 下午3:13:05
*/
public class ListIterator implements Iterator {
private ArrayList list;
private int index = 0;
public ListIterator(ArrayList list) {
// TODO Auto-generated constructor stub
this.list = list;
}
@Override
public Object next() {
Object object = list.get(index);
index = index + 1;
return object;
}
public boolean hasNext() {
if (index >= list.size()) {
return false;
} else {
return true;
}
}
}
容器的实现类:
/*
* @author zzf
* @date 2018年10月25日 下午2:57:56
*/
public class ArrayDemo implements Container {
public User[] user;
public ArrayDemo(User[] user) {
this.user=user;
}
@Override
public Iterator getIterator() {
return new ArrayIterator(user);
}
}
/*
* @author zzf
* @date 2018年10月25日 下午3:11:00
*/
public class ListDemo implements Container{
private ArrayList list;
public ListDemo(ArrayList list) {
this.list=list;
}
@Override
public Iterator getIterator() {
// TODO Auto-generated method stub
return new ListIterator(list);
}
}
测试类:
/*
* @author zzf
* @date 2018年10月25日 下午3:03:05
*/
public class IteratorPatternDemo {
public static void main(String[] args) {
User user[] = { new User("a",1),new User("b",2),new User("c",3) };
ArrayList list=new ArrayList();
list.add(new User("a",1));
list.add(new User("b",2));
list.add(new User("c",3));
Container arrs = new ArrayDemo(user);
Container lists = new ListDemo(list);
for (Iterator iter = arrs.getIterator(); iter.hasNext();) {
User users = (User) iter.next();
System.out.println("Name : " + users);
}
System.out.println("--------------------------------------");
for (Iterator iter = lists.getIterator(); iter.hasNext();) {
User users = (User) iter.next();
System.out.println("Name : " + users);
}
}
}
十二:组合模式
组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作树、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理。
注意事项:定义时为具体类。
实现:(迭代器模式+组合)
首先定义菜单组件,里面有组合方法和操作方法供菜单项使用。
/*
* 菜单组件(为叶节点和组合节点提供共同的接口)
* @author zzf
* @date 2018年10月26日 上午10:09:21
*/
import java.util.Iterator;
public abstract class MenuComponent {
//组合方法,新增删除和菜单组件
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//操作方法,由菜单项使用。
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print(int i) {
throw new UnsupportedOperationException();
}
public Iterator<MenuComponent> createIterator() {
throw new UnsupportedOperationException();
}
}
组合类:
/*
* 组合类(菜单)
* @author zzf
* @date 2018年10月26日 上午10:17:43
*/
import java.util.ArrayList;
import java.util.Iterator;
public class Menu extends MenuComponent {
ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print(int i) {
System.out.println();
System.out.print(i);
for(int n=i;n>0;n--)
System.out.print(" ");
System.out.print(getName());
System.out.println(":" + getDescription());
System.out.println("--------------------");
//使用迭代器遍历所有菜单组件,并且打印
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
menuComponent.print(i+1);
}
}
public Iterator<MenuComponent> createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
}
菜单类:(该类为叶类不会有节点)
/*
* 菜单项,这是叶类,它实现了组合内元素的行为
* @author zzf
* @date 2018年10月26日 上午10:14:56
*/
import java.util.Iterator;
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name, String description,
boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print(int i) {
System.out.print(i);
for(int n=i;n>0;n--)
System.out.print(" ");
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.print(": " + getPrice());
System.out.println(" 描述-- " + getDescription());
}
public Iterator<MenuComponent> createIterator() {
return new NullIterator();
}
}
这里用到了空迭代器类:
/*
* 空迭代器类
* @author zzf
* @date 2018年10月26日 上午11:28:09
*/
import java.util.Iterator;
public class NullIterator implements Iterator<MenuComponent> {
public MenuComponent next() {
return null;
}
public boolean hasNext() {
return false;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
一个迭代器类:
/*
* @author zzf
* @date 2018年10月26日 上午11:29:45
*/
import java.util.Iterator;
import java.util.Stack;
public class CompositeIterator implements Iterator<MenuComponent> {
Stack<Iterator<MenuComponent>> stack = new Stack<Iterator<MenuComponent>>();
public CompositeIterator(Iterator<MenuComponent> iterator) {
stack.push(iterator);
}
public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator<MenuComponent> iterator = (Iterator<MenuComponent>)stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext(); //递归调用
} else {
return true;
}
}
}
public MenuComponent next() {
if (hasNext()) {
Iterator<MenuComponent> iterator = (Iterator<MenuComponent>)stack.peek();
MenuComponent component = (MenuComponent)iterator.next();
if (component instanceof Menu) {
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
客户端类:
/*
* 客户类
* @author zzf
* @date 2018年10月26日 上午10:29:47
*/
import java.util.Iterator;
public class Client {
//顶层菜单组件
MenuComponent allMenus;
public Client(MenuComponent allMenus) {
this.allMenus = allMenus;
}
//只需要调用最顶层菜单的print(),就可以打印整个菜单层次,包括所有菜单以及所有菜单项
public void printMenu(int i) {
allMenus.print(i);
}
public void printVegetarianMenu(int i) {
Iterator<MenuComponent> iterator = allMenus.createIterator();
System.out.println("\nVEGETARIAN MENU\n----");
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
try {
if (menuComponent.isVegetarian())
menuComponent.print(1);
} catch (UnsupportedOperationException ex) {}
}
}
}
测试类:
/*
* @author zzf
* @date 2018年10月26日 上午10:35:49
*/
public class MenuTestDrive {
public static void main(String[] args) {
MenuComponent pancakeHouseMenu = new Menu("煎饼", "早餐");
MenuComponent dinerMenu = new Menu("快餐", "午餐");
MenuComponent cafeMenu = new Menu("小餐馆", "正餐");
MenuComponent dessertMenu = new Menu("甜点", "甜点");
MenuComponent coffeeMenu = new Menu("咖啡", "下午咖啡搭配");
MenuComponent allMenus = new Menu("菜单", "所有菜单");
//将其他节点挂到allMenus上
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
pancakeHouseMenu.add(new MenuItem("手抓饼2号", "手抓饼配炒蛋和吐司", true, 2.99));
pancakeHouseMenu.add(new MenuItem("手抓饼1号", "手抓饼加煎蛋,香肠", false, 2.99));
pancakeHouseMenu.add(new MenuItem("手抓饼3号","手抓饼加蓝莓和蓝莓糖浆", true, 3.49));
pancakeHouseMenu.add(new MenuItem("华夫饼", "华夫饼,你可以选择蓝莓或草莓", true, 3.59));
dinerMenu.add(new MenuItem("全麦面包", "全麦面包配生菜和西红柿培根", false, 2.99));
dinerMenu.add(new MenuItem("老火炖汤", "每天一碗汤,外加一份土豆沙拉",false, 3.29));
dinerMenu.add(new MenuItem("热狗", "一个热狗,加上酸辣香肠,调味料,洋葱,再加上奶酪", false, 3.05));
dinerMenu.add(new MenuItem("蒸蔬菜和糙米", "蒸蔬菜配糙米", true, 3.99));
dinerMenu.add(new MenuItem("意大利面食", "意大利面加马里纳拉酱和一片酸面包", true, 3.89));
dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem("苹果派", "苹果派,脆皮,上面有香草冰淇淋", true, 1.59));
dessertMenu.add(new MenuItem("奶酪蛋糕", "纽约奶油芝士蛋糕,巧克力全麦面包", true, 1.99));
dessertMenu.add(new MenuItem("冰沙","一勺覆盆子和一勺酸橙",true,1.89));
cafeMenu.add(new MenuItem("素食汉堡和空气薯条","素食汉堡配全麦面包、生菜、西红柿和薯条",true,3.99));
cafeMenu.add(new MenuItem("老火炖汤","一杯当天的汤,外加一份沙拉",false,3.69));
cafeMenu.add(new MenuItem("玉米煎饼","一份大份的墨西哥卷饼,配上整颗平托豆、莎莎酱和鳄梨酱",true,4.29));
cafeMenu.add(coffeeMenu);
// cafeMenu.add(coffeeMenu);
coffeeMenu.add(new MenuItem("早餐点心","松脆的蛋糕,上面有肉桂和核桃",true,1.59));
coffeeMenu.add(new MenuItem("百吉饼","口味包括芝麻,罂粟籽,肉桂葡萄干,南瓜",false,0.69));
coffeeMenu.add(new MenuItem("意大利脆饼","三个杏仁或榛子脆饼",true,0.89));
Client client = new Client(allMenus);
client.printVegetarianMenu(1);
client.printMenu(1);
}
}
例二(组合模式)
import java.util.ArrayList;
import java.util.List;
/*
* @author zzf
* @date 2018年10月26日 下午1:28:11
*/
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
// 构造函数
public Employee(String name, String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates() {
return subordinates;
}
public String toString() {
return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary + " ]");
}
}
import java.util.List;
/*
* @author zzf
* @date 2018年10月26日 下午1:28:54
*/
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("1", "1", 30000);
Employee headSales = new Employee("2-1", "2-1", 20000);
Employee headMarketing = new Employee("2-2", "2-2", 20000);
Employee clerk1 = new Employee("3-1", "3-1", 10000);
Employee clerk2 = new Employee("3-2", "3-2", 10000);
Employee salesExecutive1 = new Employee("3-3", "3-3", 10000);
Employee salesExecutive2 = new Employee("3-4", "3-4", 10000);
Employee a = new Employee("a", "a", 10000);
Employee b = new Employee("b", "b", 10000);
Employee aa = new Employee("aa", "aa", 10000);
Employee bb = new Employee("bb", "bb", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
clerk2.add(a);
clerk2.add(b);
a.add(aa);
a.add(bb);
// 打印该组织的所有员工
System.out.println(CEO);
print(CEO);
}
public static void print(Employee employee) {
for (Employee e : employee.getSubordinates()) {
System.out.println(e);
if (!e.getSubordinates().isEmpty())
print(e);
}
}
}
要点:
1、迭代器允许访问聚合的元素,而不需要暴露它的内部内部结构。
2、迭代器将遍历聚合的工作封装进一个对象中。
3、当使用迭代器的时候,我们依赖聚合提供遍历。
4、迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制。
5、应该让一个类只分配一个责任。
6、组合模式提供一个结构,可同时包容个别对象和组合对象。
7、组合模式允许客户对个别对象以及组合对象一视同仁。
8、组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点。
9、再实现组合模式时,要根据需要平衡透明性和安全性。