JAVA 设计模式实战,工厂方法模式/虚拟构造子模式/多态性工厂

1,266 阅读4分钟
  • 非常感谢你阅读本文,欢迎【👍点赞】【⭐收藏】【📝评论】~
  • 放弃不难,但坚持一定很酷!希望我们大家都能每天进步一点点!🎉

为什么要有设计模式?

一个有价值的系统总是会因为需求的变化而变化,可能是原有需求的修改,也可能是新需求的增加。于是可怜的猿们就得修改原来的代码。好的架构和设计可以让我们的代码结构具有良好的扩展性,在满足需求变化的同时仅需要修改尽可能少的代码,可以将需求变化对原系统的影响降到很低。设计模式就是人们对于良性架构设计的经验总结。


为什么要有工厂方法模式?

上一篇我介绍了简单工厂模式,但是我发现每次增加新的产品虽然不用修改消费者(或者说客户端)角色,但是都要去修改工厂角色,于是工厂越来越复杂。一旦因为扩展新产品去修改工厂出现了BUG会影响到原有程序的正常运行,这个工厂成了一个全能的类,一个每次修改都可能波及其他产品消费者的类。


工厂方法模式的类图

工厂方法模式的类图


工厂方法模式的例子

简单工厂模式通常涉及到三个角色,即产品,生产者以及消费者,其中产品包含了抽象产品和具体产品。 而工厂方法模式中的生产者发生了改变,包含了抽象工厂角色和具体工厂角色。


产品

产品就是你要用到的工具类,一般有抽象产品和具体产品。抽象产品仅仅定义有什么功能,所以应该是接口,如果有产品的共有行为可以有抽象类,具体产品则应该对抽象产品定义的功能有具体的实现。 这里与简单工厂模式一致。

/**
 * 抽象产品:工具接口
 */
public interface ITool {
	// 使用工具
	void run(String something);
}
/**
 * 具体产品:书
 */
public class Book implements ITool {
	private final String type;

	public Book(String type) {
		this.type = type;
	}

	@Override
	public void run(String something) {
		System.out.println("阅读一本" + type + "书->" + something);
	}
}
/**
 * 具体产品:钢笔
 */
public class Pen implements ITool {
	private final String color;

	public Pen(String color) {
		this.color = color;
	}

	@Override
	public void run(String something) {
		System.out.println("使用一枝" + color + "钢笔->" + something);
	}
}

生产者

在工厂方法模式里每个生产者仅仅生产与自己相关联的产品,不再是全能工厂。 这里是与简单工厂模式最大的区别。

/**
 * 抽象生产者角色:工具商店接口
 */
public interface IToolStore {
	ITool factory();
}
/**
 * 具体生产者角色:书店
 */
public class BookStore implements IToolStore {
	private final String type;

	public BookStore(String type) {
		this.type = type;
	}

	@Override
	public ITool factory() {
		return new Book(type);
	}
}
/**
 * 具体生产者角色:钢笔店
 */
public class PenStore implements IToolStore {
	private final String color;

	public PenStore(String color) {
		this.color = color;
	}

	@Override
	public ITool factory() {
		return new Pen(color);
	}
}

消费者

消费者是事件的主体,用什么干什么都是消费者的事情。但是产品怎么生产的,和产品具体如何完成事情不应该是消费者关心的事情。消费者向工厂提出需求,仅仅是对接口功能的需求,而工厂返回的是完成功能的具体子类。 这里与简单工厂模式也有区别。

/**
 * 消费者:人
 */
public class Person {
	private final String name;

	public Person(String name) {
		this.name = name;
	}

	/**
	 * 使用工具做某事
	 * @param tool
	 * @param something
	 */
	public void doSomething(ITool tool, String something) {
		System.out.print(name + ":");
		tool.run(something);
	}

	public static void main(String[] args) {
		// 一切的开始

		// 一个苦逼的猿诞生了
		Person person = new Person("乐意");

		// 从书店和钢笔店买来书和钢笔
		// (没花钱,哈哈,其实书店和钢笔店给我什么具体类的对象我不知道也不该关心,因为工厂接口的工厂方法返回值类型声明仅仅是工具这个接口)
		IToolStore bookStore = new BookStore("数学");
		ITool      book      = bookStore.factory();
		IToolStore penStore  = new PenStore("黑色");
		ITool      pen       = penStore.factory();

		person.doSomething(book, "学习数学知识");
		person.doSomething(pen, "记录学习笔记");

		// 一切的结束
	}
}

执行结果

工厂方法模式的执行结果


JDK中的应用

抽象产品角色:java.util.Iterator 抽象工厂角色:java.util.Collection

java.util.Collection是集合类的接口,下面还有子层的抽象类比如java.util.Set,java.util.List等,当我们需要取得java.util.Iterator的实例时,仅仅需要调用工厂方法iterator,它们的子类在这里充当了具体工厂角色,而返回的java.util.Iterator的对象实例就是具体产品。我们不关心具体产品是什么类的实例,我们仅仅需要知道我们用这个产品可以对集合进行迭代。

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

/**
 * 集合测试
 */
public class CollectionTest {

	/**
	 * 打印(这里可以用统一的方法打印不同的集合做统一处理也恰恰反应了该模式的特点,即面向的都是接口,不用关心实例的实际类型)
	 * @param collection
	 */
	private static void printStringCollection(Collection<String> collection) {
		System.out.println("集合(抽象工厂)的真实类型为:" + collection.getClass());
		Iterator<String> iterator = collection.iterator();
		System.out.println("集合迭代器(抽象产品)的真实类型为:" + iterator.getClass());
		System.out.println("集合的内容如下:");
		while (iterator.hasNext()) {
			String item = iterator.next();
			System.out.println(item);
		}
		System.out.println();
	}

	public static void main(String[] args) {
		Collection<String> list = new ArrayList<>();
		list.add("1");
		list.add("2");
		list.add("3");

		Collection<String> set = new HashSet<>();
		set.add("A");
		set.add("B");
		set.add("C");

		CollectionTest.printStringCollection(list);
		CollectionTest.printStringCollection(set);
	}
}

集合测试的运行结果

集合测试的运行结果