设计模式实战应用之五:工厂方法模式

工厂方法模式的定义
        工厂方法模式的应用相当广泛。

工厂方法模式在 Java API 中的应用比比皆是:java.util.Collection 接口的 iterator 方法就是一个非常著名的工厂方法模式的演示样例;java.net.URLStreamHandlerFactory 的 createURLStreamHandler(String protocol)  也是工厂方法模式的一个非常经典的应用,URLStreamHandlerFactory 定义了一个用来创建 URLStreamHandler 实例的 createURLStreamHandler 方法,详细怎么创建?详细实现类说了算;此外。Java API 中工厂方法模式应用样例还有 java.net.ContentHandlerFactory、java.net.URL 的 openConnection 方法……。
        Gof 把工厂方法模式归类到对象创建型模式,《设计模式:可复用面向对象软件的基础》对工厂方法模式做出了明白的定义:"Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses." 翻译过来就是:"为创建一个对象定义一个接口,但把详细创建哪个类的实例留给子类决定。工厂方法同意将一个类的初始化延迟到自己的子类。"
        Why 工厂方法模式?
        通过使用工厂方法创建对象。我们能够避免client代码依赖于它所使用的接口的详细实现。client不再须要把对象创建的构造方法注入到自己的代码中去。client仅仅须要调用工厂,由工厂来决定符合要求的子类的创建过程。此外,工厂方法还能够满足每次构造不同对象、对象的构造非常复杂、对象的构造依赖详细环境等等需求。
        工厂方法模式的使用场合

  • client不知道它要创建的详细是哪一个子类。
  • 一个类想要由自己的子类来定义某对象的创建过程。
  • 类将创建某对象的职责代理给一些帮助子类中的一个,并且你想要将哪一个子类作为代理的信息进行局部化。

工厂方法模式 VS 简单工厂模式

  • 简单工厂又称"静态工厂",顾名思义。这是工厂方法和简单工厂最大的不同,这也是最easy区分开这两种模式的特征。
  • 尽管两者都志在对象创建的局部封装。但简单工厂側重于创建对象的代码复用,或者已创建实例的复用。或者创建实例的统一性;而工厂方法側重于子类自己特定创建逻辑的实现。

  • 简单工厂模式中的工厂类是为产品类实例化的核心,而工厂方法模式把初始化工作交给子类实现。

    换句话讲,假设有新产品加入,简单工厂须要改动自己的工厂类。而工厂方法仅仅需添加新工厂子类 - 简单工厂对 OCP 原则的支持力度不如工厂方法。

《Java Web 应用调用 C++ 加解密方法》需求
        众所周知,Java 对文件的加解密效能不如底层的 C/C++。

为了提高程序性能,我们要用 Java 进行 JNI 调用底层的加解密方法。
        《Java Web 应用调用 C++ 加解密方法》分析
        既然牵涉底层调用。就应该考虑到程序跨平台的问题。另外,应用至少要能支持 Linux 和 Windows。以后还可能会部署在 mac。并且这些平台还分为 64 位和 32 位。因此我们要为每一个平台都要准备一个加解密底层库,由于底层库位数不同并不兼容,所以每一个平台下又要有两个库:32 位一个,64 位一个。
        每一个库都应该有对应的类进行载入,比方本文工厂方法模式演示样例中的详细产品类之中的一个的 com.defonds.cloud.tool.config.encrypt.EncryptorLinuxAmd64.java。也就是我们的加解密类,这个类写完并编译好之后,使用 shell 生成 com_defonds_cloud_tool_config_encrypt_EncryptorLinuxAmd64.h 头文件,C/C++ 程序猿依据每一个平台的头文件封装加解密库。

加解密类的实例化。我们就用工厂方法模式管理起来。
        《Java Web 应用调用 C++ 加解密方法》类设计

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGVmb25kcw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="加解密需求类图" align="middle" />
        《Java Web 应用调用 C++ 加解密方法》源代码
        client类 EncryptorUtil 源代码:

package com.defonds.cloud.tool.config.util;

import com.defonds.cloud.tool.config.encrypt.Encryptor;
import com.defonds.cloud.tool.config.encrypt.factory.EncryptorFactory;
import com.defonds.cloud.tool.config.encrypt.factory.EncryptorFactoryLinux;
import com.defonds.cloud.tool.config.encrypt.factory.EncryptorFactoryMac;
import com.defonds.cloud.tool.config.encrypt.factory.EncryptorFactoryWindows;

public class EncryptorUtil {
	private static String osName = System.getProperties().getProperty("os.name");
	private static Encryptor encryptor;
	static {
		EncryptorFactory encryptorFactory;
		if (osName.equals("Linux")) { // os is linux
			encryptorFactory = new EncryptorFactoryLinux();
		} else if (osName.contains("Windows")) { // os is windows
			encryptorFactory = new EncryptorFactoryWindows();
		} else { // os is mac
			encryptorFactory = new EncryptorFactoryMac();
		}
		encryptor = encryptorFactory.getEncryptor();
	}

	/**
	 *
	 * @param content - the string to be encrypt or decrypt
	 * @param flag - encrypt flag, true is encrypt, false is decrypt
	 * @return - string after encrypt or decrypt
	 */
	public static String encryptorStr(String content, boolean flag) {
		return EncryptorUtil.encryptor.encrypt(content, flag);
	}

}

产品 Encryptor 接口:

package com.defonds.cloud.tool.config.encrypt;

public interface Encryptor {
	/**
	 * @param content str to be encrypted
	 * @param flag true:encrypt false:decrypt
	 * @return
	 */
	public String encrypt(String content, boolean flag);
}

详细产品类之中的一个 EncryptorLinuxAmd64 源代码(依据 EncryptorLinuxAmd64 写好的 libaesjni.so 库已放在 classpath 下的 /com/defonds/cloud/tool/config/encrypt/native/linux/amd64 文件夹中):

package com.defonds.cloud.tool.config.encrypt;

import java.io.IOException;

import com.defonds.cloud.tool.config.util.NativeUtils;

public class EncryptorLinuxAmd64 implements Encryptor {
	// Native method declaration
    // use the keyword "native" indicate this is an ‘unsafe‘ mtehod for java
    native String encryptStr(String content, boolean flag);

    // Load the library
    static {
        try {
    		System.loadLibrary("libaesjni");
    	}catch (UnsatisfiedLinkError e) {
            try {
				NativeUtils.loadLibraryFromJar("/com/defonds/cloud/tool/config/encrypt/native/linux/amd64/libaesjni.so");
			} catch (IOException e1) {
				throw new RuntimeException(e1);
			} // during runtime. .DLL within .JAR
    	}
    }

	@Override
	public String encrypt(String content, boolean flag) {
		// TODO Auto-generated method stub
		return this.encryptStr(content, flag);
	}
}

工厂接口 EncryptorFactory:

package com.defonds.cloud.tool.config.encrypt.factory;

import com.defonds.cloud.tool.config.encrypt.Encryptor;

public interface EncryptorFactory {
	public Encryptor getEncryptor();
}

详细工厂类之中的一个 EncryptorFactoryLinux 源代码:

package com.defonds.cloud.tool.config.encrypt.factory;

import com.defonds.cloud.tool.config.encrypt.Encryptor;
import com.defonds.cloud.tool.config.encrypt.EncryptorLinuxAmd64;
import com.defonds.cloud.tool.config.encrypt.EncryptorLinuxI386;

public class EncryptorFactoryLinux implements EncryptorFactory {
	private static String osArch = System.getProperties().getProperty("os.arch");

	@Override
	public Encryptor getEncryptor() {
		if (osArch.equals("amd64")) { // os is linux amd64
			return new EncryptorLinuxAmd64();
		} else { // os is linux i386
			return new EncryptorLinuxI386();
		}
	}

}

《Java Web 应用调用 C++ 加解密方法》測试
        測试代码例如以下:

		String abc = "abc";
		System.out.println("before encrypt:" + abc);
		String abcEncrypted = EncryptorUtil.encryptorStr(abc, true);
		System.out.println("after encrypt:" + abcEncrypted);
		String abc2 = EncryptorUtil.encryptorStr(abcEncrypted, false);
		System.out.println("after decrypt:" + abc2);

执行结果:
before encrypt:abc
after encrypt:3a5dd7db74fdab404e980805b1998e81
after decrypt:abc
        后记 1
        读者或许会发现。《Java Web 应用调用 C++ 加解密方法》用简单工厂模式也全然能够实现,甚至可能效果会更好(由于不用每次都 new 工厂对象了):

public class EncryptorFactory {
	private static Properties sp = System.getProperties();
	private static String osName = sp.getProperty("os.name");
	private static String osArch = sp.getProperty("os.arch");
	private static int index2 = osName.indexOf("indows");
	private static boolean isWindows = false;
	static {
		if (index2 != -1) {
			isWindows = true;
		}
    }

	public static Encryptor getEncryptor() {
		if (isWindows) { // os is windows
			if (osArch.equals("amd64")) { // os is windows amd64
				return new EncryptorWindowsAmd64();
			} else { // os is windows x86
				return new EncryptorWindowsX86();
			}
		} else { // os is linux
			if (osArch.equals("amd64")) { // os is linux amd64
				return new EncryptorLinuxAmd64();
			} else { // os is linux i386
				return new EncryptorLinuxI386();
			}
		}
	}
}

本来嘛,简单工厂就是一个工厂方法的特例,GOF 甚至在 23 种设计模式里面就没有对其进行说明,并且非常多文献里面也没有简单工厂的概念。那么究竟什么时候用简单工厂呢?笔者建议,假设你能预測到所有产品类的情况。就用简单工厂。否则就工厂方法

后记 2
        执行 jni 时,假设遇到相似于 java.lang.UnsatisfiedLinkError: /tmp/libaesjni_linux6345613888084265218.so: /tmp/libaesjni_linux6345613888084265218.so: wrong ELF class: ELFCLASS32 (Possible cause: architecture word width mismatch) 的错误:
Exception in thread "main" java.lang.UnsatisfiedLinkError: /tmp/libaesjni_linux6345613888084265218.so: /tmp/libaesjni_linux6345613888084265218.so: wrong ELF class: ELFCLASS32 (Possible cause: architecture word width mismatch)
        at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1747)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1643)
        at java.lang.Runtime.load0(Runtime.java:787)
        at java.lang.System.load(System.java:1022)
        at com.defonds.cloud.tool.config.util.NativeUtils.loadLibraryFromJar(NativeUtils.java:91)
        at com.defonds.cloud.tool.config.encrypt.EncryptorLinux.<clinit>(EncryptorLinux.java:20)
        at com.defonds.cloud.tool.config.util.EncryptorFactory.getEncryptor(EncryptorFactory.java:24)
        at com.defonds.cloud.tool.config.ConfigUtil.<clinit>(ConfigUtil.java:7)
        at TestTime.main(TestTime.java:26)
        解决的方法:分别提供 64 位和 32 位库。
        后记 3
        假设 jni 时遇到下面错误:
java: symbol lookup error: /tmp/libaesjni4570314921835538044.so: undefined symbol: aesrun
java: symbol lookup error: /tmp/libaesjni8667398299924347273.so: undefined symbol: GetStringUTFChars
        解决的方法:这是 C++ 调用了 C 的函数,编译规则不一样造成,把 C++ 代码 (cpp 后缀) 所有换成 C 代码,又一次编译就好了。
        參考资料

时间: 2024-10-14 10:11:53

设计模式实战应用之五:工厂方法模式的相关文章

设计模式(二)---工厂方法模式

设计模式(二)---工厂方法模式 工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品. 抽象工厂源代码 public interface Creator { /** * 工厂方法 */ public Product factory(); } 抽象产品

设计模式(二)——工厂方法模式

设计模式(二)--工厂方法模式 一.工厂方法模式简介 1.工厂方法模式简介 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法将一个类的实例化延迟到其子类. 对每一个子类产品都分别对应一个工厂子类,用来创建相应的产品,若增加了新的产品,只需相应增加工厂子类即可. 工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得工厂方法可以被子类继承. 工厂方法模式特点: (1)工厂方法模式是对简单工厂模式的稍微的改进.工厂方法模式的用意是定义一个

设计模式C#实现(八)——工厂方法模式和简单工厂

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method使一个类的实例化延迟到其子类. 构成: 1.Product工厂方法创建的对象的接口 2.ConcreteProduct具体的产品,实现Product接口 3.Creator声明工厂方法,返回Product 4.ConcreteCreator实现/重写Creator的工厂方法,返回ConcreteProduct实例 UML类图: 实现 要生产的产品都实现一个接口 interface Product {

从王者荣耀看设计模式(十四.工厂方法模式)

从王者荣耀看设计模式(工厂方法模式) 二.简介 王者荣耀游戏设计师根据英雄技能.属性.天赋等因素,将英雄划分为射手.辅助.打野.法师.坦克.战士等职业.一局比赛存在多类英雄可供选择.玩家在挑选游戏英雄时,合理的英雄职业搭配是赢得游戏胜利的基本保证. 三.工厂方法模式 工厂方法模式(Factory Method Pattern):工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式.在

Java设计模式菜鸟系列(四)工厂方法模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39760895 工厂方法模式(Factory Method) 工厂方法:顾名思义,就是调用工厂里的方法来生产对象(产品)的. 工厂方法实现方式有3种: 一.普通工厂模式.就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建. 1.uml建模图: 2.代码实现 /** * 示例(一):普通工厂方法 * * 缺点:如果传递的字符串出错,则不能正确创建对象 */ interface Sen

设计模式(4)---工厂方法模式

1.简单工厂模式(simple factory)    简单工厂模式属于创建型模式,又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类. 1 package SimpleFactory; 2 3 public abstract class Animal { 4 5 public abstract void eat(); 6 7 } 1 package SimpleFactory; 2 3

设计模式之二:工厂方法模式

工厂方法模式(Factory Method): 定义:Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses(定义一个用于创建对象的接口,让子类决定实例化哪一个类. 工厂方法使一个类的实例化延迟到其子类). 在工厂方法模式中,抽象类Pr

设计模式笔记6:工厂方法模式

1.1 定义 简单工厂增加计算功能需要增加case语句修改工厂类.违背了开放封闭原则. 工厂方法模式:定义一个创建对象的接口,让子类决定实例化哪一个类. 1.2 类图 工厂模式:抽象出一个工厂父类,每一个功能做为一个子工厂.这时候要增加一个计算功能,要在计算类增加一个功能类,并且增加对应的工厂类.这样避免了改变工厂类,而是通过扩展的方法来新增功能.

设计模式之禅之工厂方法模式

工厂方法模式的定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到子类 样例:女娲通过八卦炉造白种人.黄种人.黑种人. 见代码: //工厂要生产的人的接口 public interface Human{ //有两个方法 public void getColor(); public void talk(); } //黑种人 public class BlackHuman implements Human { @Override public void getC

设计模式那点事--工厂方法模式

概念: 工厂方法模式定义了一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 组成角色: 在简单工厂模式中,我们把动态创建具体产品类对象放在工厂类.由于它负责具体产品对象的分支判断,容易产生高耦合.根据依赖倒转原则,我们在此基础上,把工厂类抽象为一个接口,然后让具体工厂去实现该接口方法.组成角色为: 抽象工厂,具体工厂,抽象产品,具体产品. 例子: 一个鲜活简单的例子总能让人轻松地理解晦涩的概念.我们来看看一个关于汽车价格的工厂方法模式. 我们知道,汽车的品