黑马程序员:我对工厂模式的理解

---------------------- android培训、java培训、期待与您交流! ----------------------

  1.  从 交通工具 Car开始:

              

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 cars = new ArrayList(); 则就能返回多个Car对象的实例,这就是“多例模式”雏形。(类似于JDBC中的连接池)

问题:我想添加新的交通工具类型(比如是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

你可能感兴趣的:(随便聊,class,交通,string,测试,工具,object)