一、Template 模式-将具体处理交给子类
1. 场景
某一个简单程序,用于将字符或者字符串循环输出 5 次
2. UML
3. Code
3.1 模板模式 定义在 AbstractDisplay中
/**
* @author CSZ
*/
public abstract class AbstractDisplay {
public abstract void open();
public abstract void print();
public abstract void close();
// 以下就是模板方法
public final void display(){
open();
for (int i = 0; i < 5; i++) {
print();
}
close();
}
}
3.2 第一个子类 字符处理
/**
* @author CSZ
*/
public class CharDisplay extends AbstractDisplay{
private char ch;
public CharDisplay(char ch) {
this.ch = ch;
}
@Override
public void open() {
System.out.print("<<");
}
@Override
public void print() {
System.out.print(ch);
}
@Override
public void close() {
System.out.println(">>");
}
}
3.3 第二个子类处理
/**
* @author CSZ
*/
public class StringDisplay extends AbstractDisplay{
private String string;
private int width;
public StringDisplay(@NonNull String string) {
this.string = string;
this.width = string.getBytes().length;
}
@Override
public void open() {
printLine();
}
@Override
public void print() {
System.out.println("|" + string + "|");
}
@Override
public void close() {
printLine();
}
private void printLine(){
System.out.print("+");
for (int i = 0; i < width; i++) {
System.out.print("-");
}
System.out.println("+");
}
}
3.4 测试方法
/**
* @author CSZ
*/
public class MainTest {
public static void main(String[] args) {
AbstractDisplay display1 = new CharDisplay('H');
AbstractDisplay display2 = new StringDisplay("Hello");
display1.display();
display2.display();
}
}
测试效果
<<HHHHH>>
+-----+
|Hello|
|Hello|
|Hello|
|Hello|
|Hello|
+-----+
根据UML 加深理解
- 在一个类中,我们定义了很多操作,比如业务逻辑类,有时常用的一部分逻辑我们抽取公共部分,抽成一个方法,对于一个对象,如果他想实现某个功能,他每次都要先调用A方法再调用B方法,然后补充一些信息在调用C方法,顺序是一直的只不过ABC会因为对象的不同,而有细微差异,此时就适合使用 模板模式。
- 另一方面,所有的方法,由于父类定义了骨架,具体实现交由子类,我们就必须使两者紧密联系起来,在不知道父类的源码下,想编写子类是不太可能的。
3.父类与子类要保持一致,对于构造函数中的输入值,其实保存在了 父类变量中,无论保存着是哪个子类的实例,程序都可以正常运行。 - 这里书中提出了一种有意思的思考方向:通常,我们会站在子类的角度,子类继承了父类的属性和方法,子类还可以定义自己的方法。如果我们站在父类的角度呢?角度一变,我们在父类中只声明了抽象方法,并且把实现交给了子类,换言之,就是我父类要做的事情只声明,而实行延迟到交给子类去做。所以这种交由子类实现也可以看作,子类责任。
二、Factory 模式(简单工厂)-将实例的生成交给子类
1. 需求分析
这里的工厂模式是简单工厂模式,实际上是连创建对象的过程也下方给了子类。
2. UML
3. Code
3.1简单工厂-实际上是基于 模板模式
/**
* @author CSZ
*/
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);
}
3.2 抽象类声明了方法
/**
* @author CSZ
*/
public abstract class Product {
public abstract void use();
}
3.3 具体工厂
/**
* @author CSZ
*/
public class IDCardFactory extends Factory {
private List owners = new ArrayList();
@Override
protected Product createProduct(String owner) {
return new IDCard(owner);
}
@Override
protected void registerProduct(Product product) {
owners.add(((IDCard)product).getOwner());
}
// 体现作为实际的工厂,还可以额外补充一些操作,类似于 spring 中 context.getBeanDefinitionNames();
// public List getOwners(){
// return owners;
// }
}
3. 4 抽象类 Product 的实现类
/**
* @author CSZ
*/
public class IDCard extends Product {
private String owner;
IDCard(String owner) {
System.out.println("制作" + owner + "的 ID 卡");
this.owner = owner;
}
@Override
public void use() {
System.out.println("使用" + owner + "的 ID 卡");
}
public String getOwner(){
return owner;
}
}
3.5 测试类
/**
* @author CSZ
*/
public class MainTest {
public static void main(String[] args) {
Factory factory = new IDCardFactory();
Product a = factory.create("A");
Product b = factory.create("B");
Product c = factory.create("C");
a.use();
b.use();
c.use();
}
}
/**
* @author CSZ
*/
public class MainTest {
public static void main(String[] args) {
// 父类引用指向子类的实例,当我们有了新的工厂产生新的产品时,直接替换工厂即可
Factory factory = new IDCardFactory();
Product a = factory.create("A");
Product b = factory.create("B");
Product c = factory.create("C");
a.use();
b.use();
c.use();
}
}
总结:
- 实际上,在 Factory 中规定了,怎样创建一个 Product 对象,简单说就是通过 create() 方法创建并返回一个Product 的对象。而在 create() 中要做几个操作,于是结合前面的模板模式学习,我们不难想出,将这几个操作的方法进行抽象,具体的实现延迟到子类去实现。
- 这里面关键的区分,Factory 是声明生产相同类型的产品对象,但是 Product 才是声明业务方法的主体。于是当我们的工厂和抽象产品完后,我们要创建具体的工厂,具体的产品,本案例中使用的是 IDCard。
Product 产品之一是 IDCard。正如标题所说的,交由子类,Product 交给子类的责任就是 具有 use() 这个方法的责任,所以 IDCard 必须重写和实现这个方法。 - 另外,值得注意的是,在 Factory类下的 createProduct 方法,有几种实现手段:
- 指定为抽象方法,就是 Code 中的实现方式
- 默认实现:在方法体内 直接 return new Product(name) ; 但是由此 Product 类,不能为抽象。
- 抛出异常:如果使用默认的父类 create 方法,内部抛出异常,因为我们希望子类来重写该方法。