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

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

基本的结构是这样的:

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

下面是示例代码:

抽象产品(接口)

public interface Product {

	public void doSomething();
}

具体产品实现类

public class ProductA implements Product {

	@Override
	public void doSomething() {
		System.out.println("Product A do something.");
	}

}

public class ProductB implements Product {

	@Override
	public 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 should be parameterized

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

下面是一个简单的:

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;
	}
}

只有实现了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 getAllClassesByInterface(Class c) {
		List returnClassList = new ArrayList();
		
		if (c.isInterface()) {
			String packageName = c.getPackage().getName();
			try {
				List 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 getClasses(String packageName) throws ClassNotFoundException, IOException {
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
		String path = packageName.replace('.', '/');
		Enumeration resources = cl.getResources(path);
		List dirs = new ArrayList();
		while (resources.hasMoreElements()) {
			URL resource = resources.nextElement();
			dirs.add(new File(resource.getFile()));
		}
		
		List classes = new ArrayList();
		for (File directory : dirs) {
			classes.addAll(findClasses(directory, packageName));
		}
		
		return classes;
	}
	
	private static List findClasses(File directory, String packageName) throws ClassNotFoundException {
		
		List classes = new ArrayList();
		
		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 for
		
		return classes;
	}
}

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

下面是改过后的ProductFactory类:

import java.util.List;
import java.util.Random;


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 static Product createProduct() {
		
		Product prod = null;
		
		List concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);
		
		Random r = new Random();
		int rand = r.nextInt(concreteProductList.size());
		prod = createProduct(concreteProductList.get(rand));
		
		return prod;
	}
}

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

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

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

你还可以这么改:

import java.util.List;
import java.util.Random;

@SuppressWarnings("all")
public class ProductFactory {
	
	public static  T createProduct(Class 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 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 {
	
	public static  T createProduct(Class 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 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 {
	
	private static Map products = new HashMap();
	
	public static  T createProduct(Class 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 concreteProductList = ClassUtils.getAllClassesByInterface(Product.class);
		
		Random r = new Random();
		int rand = r.nextInt(concreteProductList.size());
		prod = (Product) createProduct(concreteProductList.get(rand));
		
		return prod;
	}
}

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

你可能感兴趣的:(Java,设计,Java技术)