有这么一个场景,女蜗造人,造了三种人类,白色人种,黑色人种,黄色人种。这里用软件设计的方法来实现,涉及到女蜗,八卦炉,三种颜色的人。
类图如下:
定义一个人类接口,人类的总称:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 人类接口
*/
public interface Human {
/**
* 每个人种的皮肤都有相应的颜色
*/
void getColor();
/**
* 人会说话交谈
*/
void talk();
}
人类接口有两个方法,一个是获取颜色,一个是交谈,这是人类的基本特性。每个人种都需要实现这个接口,实现方法。
黑色人种:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 黑色人种
*/
public class BlackHuman implements Human{
@Override
public void getColor() {
System.out.println("黑色人种的皮肤颜色是黑色的");
}
@Override
public void talk() {
System.out.println("黑人会说话,反正不是说黑话");
}
}
白色人种:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 白色人种
*/
public class WhiteHuman implements Human{
@Override
public void getColor() {
System.out.println("白色人种的皮肤是白色的");
}
@Override
public void talk() {
System.out.println("白色人种会说话,一般说英语");
}
}
黄色人种:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 黄色人种
*/
public class YellowHuman implements Human{
@Override
public void getColor() {
System.out.println("中国人是黄色人种,皮肤是黄色的,称黄种人");
}
@Override
public void talk() {
System.out.println("黄种人的通用语言是普通话,我们一起来说普通话");
}
}
还需要定义一个抽象类,里面有一个抽象方法用来创造三种人类,代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 抽象工厂
*/
public abstract class AbstractHumanFactory {
public abstract <T extends Human> T createHuman(Class<T> c);
}
定义一个创造人类的工厂,继承上面的抽象类,代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 人类工厂
*/
public class HumanFactory extends AbstractHumanFactory{
@Override
public <T extends Human> T createHuman(Class<T> c) {
//定义一个生产的人种
Human human = null;
try {
//使用反射创建对象
human = (T) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
System.out.println("工厂生产人种错误");
}
return (T) human;
}
}
有了上面的基础,下面女蜗就可以进行造人了,代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 女蜗,使用工厂类生产人类
*/
public class NvWa {
public static void main(String[] args) {
//声明阴阳八卦炉
AbstractHumanFactory abstractHumanFactory = new HumanFactory();
//女蜗第一次造人,火候不足,于是造出了白种人
System.out.println("造成的第一批人是白色人种");
Human whiteHuman = abstractHumanFactory.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
//女蜗第二次造人,火候过了,于是造出了黑种人
System.out.println("造成的第二批人是黑色人种");
Human blackHuman = abstractHumanFactory.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();
//女蜗第三次造人,火候刚刚好,于是造出了黄种人
System.out.println("造成的第三批人是黄色人种");
Human yellowHuman = abstractHumanFactory.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
运行程序,输出如下:
D:\programing\java1.8\jdk1.8.0_131\bin\java "-javaagent:D:\mysoftware\JetBrains\IntelliJ IDEA 2017.1.2\lib\idea_rt.jar=51231:D:\mysoftware\JetBrains\IntelliJ IDEA 2017.1.2\bin" -Dfile.encoding=UTF-8 -classpath D:\programing\java1.8\jdk1.8.0_131\jre\lib\charsets.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\deploy.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\javaws.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\jce.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\jfr.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\jsse.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\management-agent.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\plugin.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\resources.jar;D:\programing\java1.8\jdk1.8.0_131\jre\lib\rt.jar;F:\mystudy\springboot\designpattern\target\classes;D:\programing\mavenrepository\org\springframework\boot\spring-boot-starter-web\1.5.6.RELEASE\spring-boot-starter-web-1.5.6.RELEASE.jar;D:\programing\mavenrepository\org\springframework\boot\spring-boot-starter\1.5.6.RELEASE\spring-boot-starter-1.5.6.RELEASE.jar;D:\programing\mavenrepository\org\springframework\boot\spring-boot\1.5.6.RELEASE\spring-boot-1.5.6.RELEASE.jar;D:\programing\mavenrepository\org\springframework\boot\spring-boot-autoconfigure\1.5.6.RELEASE\spring-boot-autoconfigure-1.5.6.RELEASE.jar;D:\programing\mavenrepository\org\springframework\boot\spring-boot-starter-logging\1.5.6.RELEASE\spring-boot-starter-logging-1.5.6.RELEASE.jar;D:\programing\mavenrepository\ch\qos\logback\logback-classic\1.1.11\logback-classic-1.1.11.jar;D:\programing\mavenrepository\ch\qos\logback\logback-core\1.1.11\logback-core-1.1.11.jar;D:\programing\mavenrepository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;D:\programing\mavenrepository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;D:\programing\mavenrepository\org\slf4j\log4j-over-slf4j\1.7.25\log4j-over-slf4j-1.7.25.jar;D:\programing\mavenrepository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;D:\programing\mavenrepository\org\springframework\boot\spring-boot-starter-tomcat\1.5.6.RELEASE\spring-boot-starter-tomcat-1.5.6.RELEASE.jar;D:\programing\mavenrepository\org\apache\tomcat\embed\tomcat-embed-core\8.5.16\tomcat-embed-core-8.5.16.jar;D:\programing\mavenrepository\org\apache\tomcat\embed\tomcat-embed-el\8.5.16\tomcat-embed-el-8.5.16.jar;D:\programing\mavenrepository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.16\tomcat-embed-websocket-8.5.16.jar;D:\programing\mavenrepository\org\hibernate\hibernate-validator\5.3.5.Final\hibernate-validator-5.3.5.Final.jar;D:\programing\mavenrepository\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;D:\programing\mavenrepository\org\jboss\logging\jboss-logging\3.3.1.Final\jboss-logging-3.3.1.Final.jar;D:\programing\mavenrepository\com\fasterxml\classmate\1.3.3\classmate-1.3.3.jar;D:\programing\mavenrepository\com\fasterxml\jackson\core\jackson-databind\2.8.9\jackson-databind-2.8.9.jar;D:\programing\mavenrepository\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;D:\programing\mavenrepository\com\fasterxml\jackson\core\jackson-core\2.8.9\jackson-core-2.8.9.jar;D:\programing\mavenrepository\org\springframework\spring-web\4.3.10.RELEASE\spring-web-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\springframework\spring-aop\4.3.10.RELEASE\spring-aop-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\springframework\spring-beans\4.3.10.RELEASE\spring-beans-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\springframework\spring-context\4.3.10.RELEASE\spring-context-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\springframework\spring-webmvc\4.3.10.RELEASE\spring-webmvc-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\springframework\spring-expression\4.3.10.RELEASE\spring-expression-4.3.10.RELEASE.jar;D:\programing\mavenrepository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\programing\mavenrepository\org\springframework\spring-core\4.3.10.RELEASE\spring-core-4.3.10.RELEASE.jar com.jack.factory.NvWa
造成的第一批人是白色人种
白色人种的皮肤是白色的
白色人种会说话,一般说英语
造成的第二批人是黑色人种
黑色人种的皮肤颜色是黑色的
黑人会说话,反正不是说黑话
造成的第三批人是黄色人种
中国人是黄色人种,皮肤是黄色的,称黄种人
黄种人的通用语言是普通话,我们一起来说普通话
Process finished with exit code 0
工厂方法模式的优点:
1,良好的封装性,代码结构清晰,一个对象创建是由条件约束的。
2,工厂方法模式的扩展比较好,在增加产品类的情况下,只要适当的修改具体的工厂类或扩展一个工厂类。
3,屏蔽产品类,不需要关心产品类的具体逻辑和实现,只要接口不变。
4,实现解耦框架。
工厂方法模式的扩展:
1,缩小为简单工厂模式:
考虑这样的一个场景,一个模块只需要一个工厂类,没必要把他生产出来,使用静态的方法就可以了,根据这样的要求我们修改下工厂的代码,类图如下:
代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 简单工厂模式的工厂类
*/
public class HumanFactoryB {
public static <T extends Human> T createHuman(Class<T> c) {
//定义一个生产的人种
Human human = null;
try {
//使用反射创建对象
human = (T) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
System.out.println("工厂生产人种错误");
}
return (T) human;
}
}
修改女蜗造人的代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 女蜗,使用工厂类生产人类
*/
public class NvWaB {
public static void main(String[] args) {
//女蜗第一次造人,火候不足,于是造出了白种人
System.out.println("造成的第一批人是白色人种");
Human whiteHuman = HumanFactoryB.createHuman(WhiteHuman.class);
whiteHuman.getColor();
whiteHuman.talk();
//女蜗第二次造人,火候过了,于是造出了黑种人
System.out.println("造成的第二批人是黑色人种");
Human blackHuman = HumanFactoryB.createHuman(BlackHuman.class);
blackHuman.getColor();
blackHuman.talk();
//女蜗第三次造人,火候刚刚好,于是造出了黄种人
System.out.println("造成的第三批人是黄色人种");
Human yellowHuman = HumanFactoryB.createHuman(YellowHuman.class);
yellowHuman.getColor();
yellowHuman.talk();
}
}
运行结果和之前的一样就不贴运行结果了。简单工厂模式的类图变简单了,而且调用者也简单,该模式是工厂方法模式的弱化,因为简单,所以称为简单的工厂模式,也叫做静态工厂模式。缺点就是工厂类的扩展比较困难,不符合开闭原则,但仍然是一个非常实用的设计模式。
2,升级为多个工厂类
有这样的一个场景,当一个比较复杂的项目时,经常遇到初始化一个对象比较耗精力的情况,所以所有的产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。比如创建一个对象,还需要对对象进行一些初始化的设值,一个产品类有10个具体实现,如果写在一个工厂方法中,会导致该方法巨大无比。
考虑到需要结构清晰,我们就为每个产品定义一个创造者,然后由调用者致敬去选择与哪个工厂方法关联,类图如下:
定义一个抽象类:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 多工厂模式下的抽象工厂
*/
public abstract class AbstractHumanFactoryB {
public abstract Human createHuman();
}
定义一个生产黄种人的工厂类:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
* 生产黄种人的工厂
*/
public class YellowHumanFactory extends AbstractHumanFactoryB{
@Override
public Human createHuman() {
return new YellowHuman();
}
}
生产其他人种的工厂和上面的类似。
然后在使用的时候,就使用具体的工厂类的对象的createHuman方法进行人种的生产。
3,替代单例模式
通过工厂方法,可以在内存中只生产一个对象,类图如下:
代码如下:
package com.jack.factory;
/**
* Created by jack on 2017/8/1.
*/
public class SingletonC {
//不允许在类外部通过new产生对象
private SingletonC() {
}
public void doSomething(){
// to do something
System.out.println("在这里做些什么吧!");
}
}
生产单例代码如下:
package com.jack.factory;
import java.lang.reflect.Constructor;
/**
* Created by jack on 2017/8/1.
*/
public class SingletonCFactory {
private static SingletonC singletonC;
//静态代码块,初始化单例
static{
try {
Class cl = Class.forName(SingletonC.class.getName());
//获得无参构造
Constructor constructor = cl.getDeclaredConstructor();
//设置无参构造是可访问的
constructor.setAccessible(true);
//产生一个实例对象
singletonC = (SingletonC) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取单例对象
public static SingletonC getSingletonC() {
return singletonC;
}
}
上面就通过工厂方法创建了一个单例了,利用的是java的反射机制,静态代码库的加载。
4,延迟初始化
为何延迟初始化,一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用,其通用类如如下:
代码不是太难,就不敲了,延迟加载可以用在对象初始化比较复杂的情况下,通过延迟加载降低对象的产生和销毁代理的复杂性。