• Thank you very much for reading this article. Welcome [👍 like] [⭐ collection] [📝 comment] ~
  • It’s not hard to give up, but it must be cool to hold on! I hope we can all make a little progress every day! 🎉

Why design patterns?

A valuable system will always change as requirements change, either by modifying existing requirements or by adding new ones. So the poor apes had to modify the original code. Good architecture and design can make our code structure have good expansibility, and only need to modify as little code as possible to meet the requirements change, so as to reduce the impact of requirements change on the original system to a very low level. A design pattern is a summary of people’s experience in benign architectural design.


Why the factory method pattern?

In my last article, I introduced the simple factory model, but I found that every time I added a new product, I had to change the factory role without changing the consumer (or client) role, so the factory became more and more complex. The factory becomes an omnipotent class, a class where every change can affect consumers of other products.


Class diagram of the factory method pattern


Examples of factory method patterns

The simple factory pattern usually involves three roles: product, producer, and consumer, where the product consists of abstract and concrete products. The producer in the factory method pattern has changed to include both the abstract factory role and the concrete factory role.


product

A product is a tool class that you need to use. There are usually abstract products and concrete products. Abstract products only define what functions they have, so they should be interfaces. If there are common behaviors of products that can have abstract classes, concrete products should have concrete implementations of the functions defined by the abstract products. This is consistent with the simple factory pattern.

/** * Abstract product: tool interface */
public interface ITool {
	// Use tools
	void run(String something);
}
Copy the code
/** * Specific product: book */
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("Read a book" + type + "Book - >"+ something); }}Copy the code
/** * Specific product: pen */
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("Use one" + color + "Pen - >"+ something); }}Copy the code

producers

In the factory approach, each producer produces only products related to itself, rather than an all-purpose factory. This is the biggest difference from the simple factory pattern.

/** * Abstract producer role: tool store interface */
public interface IToolStore {
	ITool factory(a);
}
Copy the code
/** * Specific producer role: bookstore */
public class BookStore implements IToolStore {
	private final String type;

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

	@Override
	public ITool factory(a) {
		return newBook(type); }}Copy the code
/** * Specific producer role: pen shop */
public class PenStore implements IToolStore {
	private final String color;

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

	@Override
	public ITool factory(a) {
		return newPen(color); }}Copy the code

consumers

The consumer is the main body of the event, and what to do with it is the consumer’s business. But how the product is made and how the product actually gets things done shouldn’t be the consumer’s concern. The consumer demands only the interface functionality from the factory, and the factory returns a concrete subclass of the completed functionality. There is also a difference from the simple factory pattern.

/** * Consumer: person */
public class Person {
	private final String name;

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

	/** * use the tool to do something *@param tool
	 * @param something
	 */
	public void doSomething(ITool tool, String something) {
		System.out.print(name + ":");
		tool.run(something);
	}

	public static void main(String[] args) {
		// The beginning of everything

		// A helpless ape was born
		Person person = new Person("Happy");

		// Buy books and pens from bookstores and pen shops
		I don't know and shouldn't care, because the factory interface's factory method return value type declaration is just the tool interface.
		IToolStore bookStore = new BookStore("Mathematics");
		ITool      book      = bookStore.factory();
		IToolStore penStore  = new PenStore("Black");
		ITool      pen       = penStore.factory();

		person.doSomething(book, "Learn math.");
		person.doSomething(pen, "Take study notes.");

		// The end of it all}}Copy the code

The execution result


Applications in the JDK

Abstract product roles: java.util.Iterator Abstract factory roles: java.util.Collection

Set, java.util.List, etc. When we need to get an instance of java.util.Iterator, we simply call the factory method Iterator. Their subclasses act as concrete factories here, and the returned object instance of java.util.Iterator is the concrete product. We don’t care what class the specific product is, we just need to know that we can iterate over the collection with this product.

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

/** * set test */
public class CollectionTest {

	/** * print (here we can print different collections in a uniform way, which is exactly the characteristic of this pattern, that is, for all interfaces, regardless of the actual type of the instance) *@param collection
	 */
	private static void printStringCollection(Collection<String> collection) {
		System.out.println(The real type of the collection (abstract factory) is: + collection.getClass());
		Iterator<String> iterator = collection.iterator();
		System.out.println(The real type of a collection iterator (abstract product) is: + iterator.getClass());
		System.out.println("The contents of the collection are as follows:");
		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); }}Copy the code

Run the results of the collection tests