模板模式和工厂模式的类别分类为“交给子类”,即两种设计模式都是通过子类来实现具体的功能。主角是子类,而不像上次的适应设计模式,主角是连接器。这种差别着重体现在我们在工业设计中对需求的不同理解和实现。
以下例子选自《图解设计模式》
一,模板模式
概念
模板模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。这种算法骨架就像模板一样,子类通过填装式重写父类方法,实现需求的功能。
应用
操作算法在这里描述很生动,比如我们将模板模式用在流程固定的操作上,比如打印字符,下单订购,注册登陆。比如spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。比如java.io.inputstream。
例子
我们通过模板打印输出字符或字符串,AbstractDisplay抽象类已经定义好了打印的步骤(open->print->close)。需要分别完善打印字符串和字符的功能。字符要类似<<char>>输出,字符串要用框框框住输出。
类图
代码
package templatePattern;
//AbstractDisplay.java
public abstract class AbstractDisplay {
public abstract void open();
public abstract void print();
public abstract void close();
public final void diasplay() {
open();
for(int i = 0; i < 5; i++) {
print();
}
close();
}
}
//CharDisplay.java
public class CharDisplay extends AbstractDisplay{
private char ch;
public CharDisplay(char ch) {
this.ch = ch;
}
public void open() {
System.out.print("<<");
}
public void print() {
System.out.print(ch);
}
public void close() {
System.out.print(">>");
}
}
//StringDisplay.java
public class StringDisplay extends AbstractDisplay{
private String str;
private int width;
public StringDisplay(String str) {
this.str = str;
width = str.getBytes().length;
}
public void open() {
printline();
}
public void print() {
System.out.println("|" + str + "|");
}
public void close() {
printline();
}
private void printline() {
System.out.print("+");
for(int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.println("+");
}
}
//Main.java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
if(str.length() < 2) {
AbstractDisplay ab = new CharDisplay(str.charAt(0));
ab.diasplay();
}else {
AbstractDisplay ab = new StringDisplay(str);
ab.diasplay();
}
sc.close();
}
}
解析
在上边的代码中有以下几个角色:
AbstractClass(抽象类)
是模板模式的模板,定义了所用的抽象方法和全部流程
ConcreteClass(具体类)
子类定义模板中的方法并实现具体功能
拓展要点
逻辑处理通用化,父类定义了算法就不需要子类相应再去定义。只要有多个子类逻辑相同的场景都可以使用模板设计模式,一般算法用final修饰,避免恶意修改。
二,工厂模式
在模板模式中,我们在父类规定处理的流程,在子类实现具体处理。如果我们将该模式用于生成实例,它就演变为工厂模式。
概念
工厂模式定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。父类决定实例的生成方式,但不决定所要生成的类,具体的处理交由子类负责来实现解耦。
应用
1. 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。4. 选择登陆/支付方式,不同的登陆方式需要不同链接和流程进行注册。
例子
用工厂模式实现一个制作并使用身份证的功能。(我觉得这个例子一般,应该再加一个制作社保卡的功能,或者居民身份证和外国人身份证分开这样的功能,不过实现思路一样)
类图如下:
代码
//Factory.java
package factoryPattern;
public abstract class Factory {
public final Product create(String owner) {
Product p = createProduct(owner);
registerProduct(p);
return p;
}
protected abstract Product createProduct(String owner);
protected abstract void registerProduct(Product product);
}
//Product.java
package factoryPattern;
public abstract class Product {
public abstract void use();
}
//IDcardFactory.java
package idCard;
import factoryPattern.*;
import java.util.*;
public class IDcardFactory extends Factory{
private ArrayList<Object> owners = new ArrayList<>();
protected Product createProduct(String owner) {
return new IDcard(owner);
}
protected void registerProduct(Product product) {
owners.add(((IDcard)product).getOwner());
}
public ArrayList<Object> getOwners() {
return owners;
}
}
//IDcard.java
package idCard;
import factoryPattern.*;
public class IDcard extends Product{
private String owner;
IDcard(String owner){
System.out.println("制作" + owner + "ID卡");
this.owner = owner;
}
public void use() {
System.out.println("使用" + owner + "ID卡");
}
public String getOwner() {
return owner;
}
}
//Main1.java
package num2;
import factoryPattern.*;
import idCard.*;
public class Main1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Factory fac = new IDcardFactory();
Product card1 = fac.create("Lisa");
Product card2 = fac.create("Lora");
card1.use();
card2.use();
}
}
解析
四个角色,抽象父类一个包,具体实现子类一个包。
Product(产品)
产品抽象类,具体由子类完成
Creator(创建者)
属于框架,负责生成产品,createProduct是负责生成产品的方法,不用new关键字而用专用方法生成实例,防止耦合。
ConcreteProduct(具体产品)
决定具体产品
ConcreteFactory(具体工厂)
决定具体工厂
拓展要点
这里面有个很重要的思想是框架和具体加工的分包,我们不需要修改框架包中的任意代码,通过具体加工的继承来实现具体功能,按照这个模式,只要符合这种工厂-产品的流程,都可以通过这种设计模式设计,具体实现内容交给子类,实现低耦合。