Design Patterns - Factory Pattern

Today, let’s talk about the factory model, a good memory is not as good as a bad blog! It is best understood with an example.

Suppose you want to make an APP , similar to the face-cut APP . It offers a lot of eyes, a lot of hairstyles to choose from. Usually, a large number of similar objects are produced in our daily life, and they are handed over to factories for mass production. Well, the theories in the world are always surprisingly similar, just as we simulate the visual working mechanism of the human brain and produce neural network algorithms. It is also feasible to move the real world factory into the code design, but that is - the factory pattern

Literally, two or three points. What exactly is the factory pattern?

Instantiate the object and replace the new operation with a factory method

intention:

Define an interface to create objects, but let subclasses decide which classes need to be instantiated, and factory methods defer instantiation to subclasses.

Suitable place:

There is a set of similar objects that need to be created

Can't foresee what class instance needs to be created at the time of coding

The system needs to consider extensibility and should not depend on the details of how product class instances are created, composed and expressed (low coupling)

As shown in the figure below, if the customer wants to request an object, we call the factory interface to instantiate a product


Let's understand with a small example

Taking the implementation of hairstyles in Face Moe as an example, we have left-biased hairstyles and right-biased hairstyles. See the example:

An excuse for generating hair, and implementing classes

/**
 * Hairstyle interface
 * @author Administrator
 */
public interface HairInterface {

	/**
	 * drawing
	 */
	public void draw();
}

 

/**
 * Right-biased
 * @author Administrator
 *
 */
public class RightHair implements HairInterface {

	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("-----------------Right-biased type-------------------");
	}
}

 

 

/**
 * Left-biased hairstyle
 * @author Administrator
 *
 */
public class LeftHair implements HairInterface {

	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("-----------------Left-biased type-------------------");
	}
}

 

public class SunnyTest {

	public static void main(String[] args){
		
//		HairInterface left = new LeftHair();
//		left.draw();
	}
}

get:

-----------------Left-biased haircuts-------------------

  

how about this! Even if it is implemented, there are still problems

1. Every time a hairstyle is added, a new class is added to implement the release interface, which is not conducive to code maintenance and management

2. On the client side, the draw method should be called explicitly

 

So what to do? ? The factory doesn't work yet! Create a new HairFactory to uniformly produce hairstyles, then the second problem above is solved. The implementation is as follows:

 

import java.util.Map;

/**
 * Hair Factory
 * @author Administrator
 */
public class HairFactory {
	/**
	 * Create an object based on the type
	 * @param key ; type keyword
	 * @return
	 */
	public HairInterface getHair(String key){
		if("left".equals(key)){
			return new LeftHair();			
		}else if("right".equals(key)){
			return new RightHair();
		}
		return null;
	}
}

 Test.java

 

		HairFactory factory = new HairFactory();
		HairInterface right =  factory.getHair("right");
		right.draw();

 get:

 

-----------------Right biased haircuts-------------------

  

So now we find that for each additional type of hairstyle, we need to add an if branch to the getHair method , which is also particularly unfavorable for management, so what should we do?

 

There is a class reflection mechanism (not detailed) in JAVA , which uses class reflection to create objects according to the class name.

In HairFactory:

 

	/**
	 * Produce an object based on the class name
	 * @param className
	 * @return
	 */
	public HairInterface getHairByClass(String className){
		
		try {
			HairInterface hair = (HairInterface) Class.forName(className).newInstance();
			return hair;
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
		}
		return null;
	}

 At this point, the full path is passed in the Test.java class

 

HairInterface left =factory.getHairByClass("com.sunny.project.LeftHair");
left.draw();

 get:

-----------------Left-biased haircuts-------------------

 By doing this, we can directly create instances based on the class name, which is conducive to code management

 

 

But on the way to keep improving, I always feel uncomfortable with such a long parameter. Is there a way to make this parameter simple and clear?

Of course there is ...

There is a properties file ( .properties ) in JAVA , which stores data in the form of key and value

 

We create a new type.properties

left=com.sunny.project.LeftHair
right=com.sunny.project.RightHair

 With this file, how to use it? This involves file operations, read the file to find the corresponding relationship

 

There is a Properties class in Java , which is used to manipulate the properties file

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * properties文件的读取工具
 * @author Administrator
 *
 */
public class PropertiesReader {

	
	public Map<String, String> getProperties() {

		Properties props = new Properties();
		Map<String, String> map = new HashMap<String, String>();
		try {

			InputStream in = getClass().getResourceAsStream("type.properties");
			props.load(in);
			Enumeration en = props.propertyNames();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				String property = props.getProperty(key);
				map.put(key, property);
//				System.out.println(key + "  " + property);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}
}

 这个时候我们的getHair方法有需要升级了

	/**
	 * 根据类的名称来生产对象
	 * @param className
	 * @return
	 */
	public HairInterface getHairByClassKey(String key){
		
		try {
			Map<String, String> map = new PropertiesReader().getProperties();
			
			HairInterface hair = (HairInterface) Class.forName(map.get(key)).newInstance();
			return hair;
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

 此时客户端实现为

		HairInterface hair = factory.getHairByClassKey("right");
		hair.draw();

 得到:

-----------------右偏分发型-------------------

 我们现在想要增加一个中分发型,要怎么办呢??

新建一个InHair类实现HairInterface

/**
 * 中分发型
 * @author Administrator
 *
 */
public class InHair implements HairInterface {

	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("-----------------中分发型-------------------");
	}
}

 然后还需要做什么呢??是不是在propertied文件中添加对应关系

in=com.sunny.project.InHair

 测试:

		HairInterface hair = factory.getHairByClassKey("in");
		hair.draw();

 得到:

-----------------中分发型-------------------

 这个工厂模式到这里就完全实现了,我们添加新的发型不需要修改HairFactory,只需要添加对应关系和相应的实现类。

 

Software entities should be open for extension,but closed for modification——Bertrand Meyer

对扩展开放,对修改关闭——“开闭原则”

 

参考资料:http://www.imooc.com/video/5316

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326355217&siteId=291194637