Java设计模式泛型化之工厂模式

来源:互联网 发布:时代网络投诉电话 编辑:程序博客网 时间:2024/05/17 04:27

关于工厂模式,本文就不详述了。大家可以百度。

基本的结构是这样的:

一个抽象产品(接口或者抽象类)几个具体的产品类去实现这个抽象产品一个工厂类一个调用类

下面是示例代码:

抽象产品(接口)

public interface Product {public void doSomething();}

具体产品实现类

public class ProductA implements Product {@Overridepublic void doSomething() {System.out.println("Product A do something.");}}

public class ProductB implements Product {@Overridepublic void doSomething() {System.out.println("Product B do something.");}}

产品工厂类

public class ProductFactory {public static void main(String[] args) {}public static Product createProduct(Class c) {Product prod = null;try {prod = (Product) Class.forName(c.getName()).newInstance();} catch (Exception e) {// TODO: handle exception}return prod;}}

工厂调用类

public class FactoryCaller {public static void main(String[] args) {Product prodA = ProductFactory.createProduct(ProductA.class);prodA.doSomething();Product prodB = ProductFactory.createProduct(ProductB.class);prodB.doSomething();}}

关于这个模式的泛型化,就在于产品工厂类里面的那个createProduct()方法了。我们都知道Class是一个raw-type,这种类型在使用的时候是要给出一个类型限定的。否则,会有一个警告。

Class is a raw type. References to generic type Class<T> should be parameterized

那么createProduct()方法要如何修改呢?

下面是一个简单的:

public class ProductFactory {public static void main(String[] args) {}public static Product createProduct(Class<? extends Product> c) {Product prod = null;try {prod = (Product) Class.forName(c.getName()).newInstance();} catch (Exception e) {// TODO: handle exception}return prod;}}

只有实现了Product接口的类,才可以传入。这样用没有问题。可是,还有另外一种创建方法。

在展示下一个示例之前,先把一个工具类抛出来,这是一个很有用的工具类。

import java.io.File;import java.io.IOException;import java.net.URL;import java.util.ArrayList;import java.util.Enumeration;import java.util.List;@SuppressWarnings("all")public class ClassUtils {public static List<Class> getAllClassesByInterface(Class c) {List<Class> returnClassList = new ArrayList<Class>();if (c.isInterface()) {String packageName = c.getPackage().getName();try {List<Class> allClasses = getClasses(packageName);for (int i = 0; i < allClasses.size(); i++) {if (c.isAssignableFrom(allClasses.get(i))) {if (!c.equals(allClasses.get(i))) {returnClassList.add(allClasses.get(i));}}}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}return returnClassList;}private static List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException {ClassLoader cl = Thread.currentThread().getContextClassLoader();String path = packageName.replace('.', '/');Enumeration<URL> resources = cl.getResources(path);List<File> dirs = new ArrayList<File>();while (resources.hasMoreElements()) {URL resource = resources.nextElement();dirs.add(new File(resource.getFile()));}List<Class> classes = new ArrayList<Class>();for (File directory : dirs) {classes.addAll(findClasses(directory, packageName));}return classes;}private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {List<Class> classes = new ArrayList<Class>();if (!directory.exists()) {return classes;}File[] files = directory.listFiles();for (File file : files) {if (file.isDirectory()) {assert !file.getName().contains(".");classes.addAll(findClasses(file, packageName + "." + file.getName()));} else if (file.getName().endsWith(".class")) {classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));}} // end forreturn classes;}}

它的作用是拿到某个接口下所有的实现类。

下面是改过后的ProductFactory类:

import java.util.List;import java.util.Random;public class ProductFactory {public static void main(String[] args) {}public static Product createProduct(Class<? extends Product> c) {Product prod = null;try {prod = (Product) Class.forName(c.getName()).newInstance();} catch (Exception e) {// TODO: handle exception}return prod;}public static Product createProduct() {Product prod = null;List<Class> concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);Random r = new Random();int rand = r.nextInt(concreteProductList.size());prod = createProduct(concreteProductList.get(rand));return prod;}}

这里可能有人要问了,为啥List<Class>里面的那个Class不做泛型的类型限定?它不是raw-type类型吗?

这是因为ClassUtils的返回是Class是针对所有的类型的,如果这里的List<Class>做了类型限定,那么操作符两边的类型就不匹配了。

那么,如果把ClassUtils类里面的作类型限定呢?这个,你可以试试看。

你还可以这么改:

import java.util.List;import java.util.Random;@SuppressWarnings("all")public class ProductFactory<T extends Product> {public static <T extends Product> T createProduct(Class<T> c) {T t = null;try {t = (T) Class.forName(c.getName()).newInstance();} catch (Exception e) {// TODO: handle exception}return t;}public static Product createProduct() {Product prod = null;List<Class> concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);Random r = new Random();int rand = r.nextInt(concreteProductList.size());prod = (Product) createProduct(concreteProductList.get(rand));return prod;}}

上面这段还是有点小问题的。你能找出来吗?


小改一下:

import java.util.List;import java.util.Random;@SuppressWarnings("all")public class ProductFactory<T extends Product> {public static <T> T createProduct(Class<T> c) {T t = null;try {t = (T) Class.forName(c.getName()).newInstance();} catch (Exception e) {// TODO: handle exception}return t;}public static Product createProduct() {Product prod = null;List<Class> concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);Random r = new Random();int rand = r.nextInt(concreteProductList.size());prod = (Product) createProduct(concreteProductList.get(rand));return prod;}}

这里和上面的例子都用了泛型强转。我们在泛型十诫里面的第一条就是不要用泛型做强转。那么,为啥要在这里用呢?因为这里的强转赋值只有一次。如果多次,且类型不同的话会出问题。那么十诫第一条是不是不妥呢?也不是,它只是告诫你,要小心,小心,再小心。


再改进下,加一个“小仓库”:

import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Random;@SuppressWarnings("all")public class ProductFactory<T extends Product> {private static Map<String, Product> products = new HashMap<String, Product>();public static <T> T createProduct(Class<T> c) {T t = null;try {if (products.containsKey(c.getSimpleName())) {t = (T) products.get(c.getSimpleName());} else {t = (T) Class.forName(c.getName()).newInstance();}} catch (Exception e) {// TODO: handle exception}return t;}public static Product createProduct() {Product prod = null;List<Class> concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);Random r = new Random();int rand = r.nextInt(concreteProductList.size());prod = (Product) createProduct(concreteProductList.get(rand));return prod;}}

如果已经创建了,直接拿出来用就可以了。

1 0