---------------------- android培训、java培训、期待与您交流! ----------------------
package com.suius.designPattern.factory;
public class Car {
public void run(){
System.out.println("车在飞奔……");
}
}
在测试类中当然可以这样调用:
package com.suius.designPattern.factory;
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.run();
}
}
问题:我禁止在测试中new 类Car的对象,而是限定在Car的内部产生Car对象,然后在测试类中通过静态方法getInstance()只能得到同一个Car对象
解决方案:使用静态工厂:(由于只产生一个Car对象,又叫“单例模式”)(静态工厂方法和简单工厂有点小冲突,最好只用其中之一)
public class Car {
private static Car car = new Car();
private Car() {
}
public static Car getInstance() {
return car;
}
public void run() {
System.out.println("车在飞奔……");
}
}
这样,在Car类的内部封装了产生Car对象的逻辑,所以这个Car就好似一个工厂,而把getInstance()叫作工厂方法。
若在Car中定义:private static List
问题:我想添加新的交通工具类型(比如是Plane),可以new Plane类,在测试类中所有关于Car的代码。如果要添加多种交通工具,显然这种做法是繁琐的。
这时可以考虑让所有的交通工具类都实现Movable接口,并重写move()方法,因此在测试类中只需要写Movable m = new Plane();这样改动的地方就少了。
问题:我想控制交通工具的创建过程,当然可以用上面的静态工厂方法,但我用下面的方法:
package com.suius.designPattern.factory;
public class PlaneFactory {
public Plane creatPlane() {
return new Plane();
}
}
当然在方法createPlane()中可以任意规定Plane对象的产生逻辑。
而在测试类中中需要写:PlaneFactory factory = new PlaneFactory(); Movable m = factory.createPlane();显然若更换其它交通工具,这两句代码都要更改:还是不理想!
这时就想到:如果要让代码替换方便,必须使用多态:用PlaneFactory实现接口VehicleFactory来统一产生交通工具对象的方法createVehicle()
package com.suius.designPattern.factory;
public class PlaneFactory implements VehicleFactory {
@Override
public Movable createVehicle() {
return new Plane();
}
}
而在测试类中只需要写:
public class Test {
public static void main(String[] args) {
PlaneFactory pf = new PlaneFactory();
Movable m = pf.createVehicle();
m.move();
}
}
这样更换交通工具时只需要更改一行代码:PlaneFactory pf = new PlaneFactory(); 即可,若添加交通工具(比如:Train),只要Train继承Vehicle,而TrainFactory继承VehicleFactory即可!
而如果使用从配置文件中将具体的工厂类型读出来,则代码就完全不用动了!:这就是“简单工厂”
2. 抽象工厂
问题:若用户需要实现一系列的操作:开车、射击、嗑瓜子……就要这样写:
package com.suius.designPattern.abstractFactory;
public class Test {
public static void main(String[] args) {
Car car = new Car();
car.run();
AK47 ak = new AK47();
ak.shoot();
Melonseed md = new Melonseed();
md.crack();
}
}
显然,若要替换交通工具,必须修改这一系列的代码,扩展性不好。这时就需要一个能产生这一系列“产品”(Car、AK47、Melonseed)的工厂
package com.suius.designPattern.abstractFactory;
public class CarSeriesFactory {
public Car createCar() {
return new Car();
}
public AK47 createAK47() {
return new AK47();
}
public Melonseed createMelonseed() {
return new Melonseed();
}
}
而这时若更改为TrainSeriesFactory,测试类中还要更改一系列的调用方法,麻烦!
有了简单工厂的经验,这里要做的是产生一个TrainSeriesFactory或者CarSeriesFactory的父类(抽象类)AbstractFactory;而Car或者Train要统一成抽象类Vehicle,AK47或者Music要统一成抽象类Fun,Melonseed或者Instantnoodles要统一成抽象类Food。
这就是面向抽象的编程!
package com.suius.designPattern.abstractFactory;
public abstract class AbstractFactory {
public abstract Vehicle createVehicle();
public abstract Fun createFun();
public abstract Food createFood();
}
package com.suius.designPattern.abstractFactory;
public class CarSeriesFactory extends AbstractFactory {
public Car createVehicle() {
return new Car();
}
public AK47 createFun() {
return new AK47();
}
public Melonseed createFood() {
return new Melonseed();
}
}
package com.suius.designPattern.abstractFactory;
public class Car extends Vehicle{
public void run() {
System.out.println("汽车飞奔……");
}
}
package com.suius.designPattern.abstractFactory;
public class Test {
public static void main(String[] args) {
AbstractFactory af = new TrainSeriesFactory();
Vehicle v = af.createVehicle();
v.run();
}
}
这种实现的结果是在测试类中只需要更改new TrainSeriesFactory() 就能实现更换所有一系列方法的功能,(若在配置文件中配置,甚至不必更改代码)
很多网站具有换皮肤的功能,和这种模式有异曲同工之妙!
这时思路看起来很混乱,只要画一下关系图即可:
总结:简单工厂与抽象工厂都不完美:
简单工厂对于添加产生一系列产品(Train、Music、Instantnoodle)的操作比较繁琐
抽象工厂对于添加某个产品的各类(如:CarSeriesFactory中增加createSleep(),需要定义新抽象类Sleep、多个实现方法),改动的地方也不少!
3. 能否将简单工厂 和 抽象工厂 的优势结合起来?
下面模拟一下spring中的实现:
将要创建对象的类 配置在文件(假如是spring.properties)中:比如:VehicleType=com.suius.designPattern.imitateSpring.Car,需要调用时再读出!
从配置文件读取数据的操作见我的博客:http://blog.csdn.net/suiusoar/article/details/7530191
这样只需要更改配置文件就可以实现更换交通工具,这样就实现了产品类型的定制!
而spring中只不过是换了一种形式,将配置文件规范了一下而已(格式是xml,穿了马甲):<beanid=”vt”class=”suiusoar.Car”>bean>
而难点就在于怎样读取xml文件中的信息:这里在网上搜索后找到了JDOM
package com.suius.designPattern.imitateSpring; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.SAXBuilder; import org.jdom2.xpath.XPath; @SuppressWarnings("deprecation") public class ClassPathXmlApplicationContext implements BeanFactory { //spring是bean容器:从xml配置文件中读取的信息全部存储到Map容器,等调用时再从容器中取出 private Map
container = new HashMap ();
public ClassPathXmlApplicationContext(String fileName) throws Exception { SAXBuilder sb = new SAXBuilder(); Document doc = sb.build(this.getClass().getClassLoader() .getResourceAsStream(fileName)); Element root = doc.getRootElement(); List list = XPath.selectNodes(root, "/beans/bean"); for (int i = 0; i < list.size(); i++) { Element bean = (Element) list.get(i); String id = bean.getAttributeValue("id"); String clazz = bean.getAttributeValue("class"); Object o = Class.forName(clazz).newInstance(); container.put(id, o); } } @Override public Object getBean(String id) { return container.get(id); } }
测试类中调用时只需要这样写:package com.suius.designPattern.imitateSpring; public class Test { public static void main(String[] args) throws Exception { BeanFactory bf = new ClassPathXmlApplicationContext( "com/suius/designPattern/imitateSpring/applicationContext.xml"); Object o = bf.getBean("V"); Movable m = (Movable) o; m.move(); } }
测试时发现的一个异常:A pseudo attribute name is expected.因为xml文件中的标签写错了:结尾的?没写另一个异常:NullPointerException 出错原因:比较恶心的一个错误,调了半天,也不知道错在哪,后来改了一下测试类中的getBean("v")就能正常运行了!我估计是调用时传入的v与xml文件中的id中的“v”大小写不一致!
这就是一个最简单的spring的模拟!
---------------------- android培训、java培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net/heima