设计模式之【抽象工厂模式】
抽象工厂模式介绍
抽象工厂模式(Abstract Factory Pattern)定义了一个抽象工厂类,其每个子类都可以对应生成一个产品族(一个工厂可以生成该产品族下的多个产品。)。
其优点在于保证了项目中所描述的每个产品都是出自同一个产品族下(工厂对产品进行了分组),且扩展一个新的产品族不难,仅需要为抽象工厂类增加多一个实现类。其缺点是产品族本身难扩展。
角色介绍
抽象工厂类(Abstract Factory )此为该模式的核心角色,提供生产每种产品的接口方法。
具体工厂类(concrete Factory)是抽象工厂类的实现类,提供生产具体产品的方法。
抽象产品类(Abstract Product)是工厂方法所生成的对象的父类或接口。
具体产品类(Concrete Product)此为具体工厂所生成的具体产品。
注:抽象产品的种类和抽象工厂的方法数相对应,如一般电脑制造商卖的产品有主机、键盘、鼠标等……对应这个抽象工厂就应该有生产主机、键盘、鼠标等的方法。而具体工厂则代表具体的电脑制造商,如惠普工厂、联想工厂等,当你获得的是惠普工厂类实例时,则调用生产出来的产品都是惠普的产品,当需求改为使用联想的产品时,只需要将惠普工厂实例改为联想工厂实例即可。
实例
场景描述
接下来我们开始模拟枪支与子弹的装配与开火,其中每种枪支对应的子弹都可能不一样(如AK47用的是7.62毫米口径的子弹而M16A4用的是5.56毫米口径),现在我们将互相匹配的枪支、子弹定义为产品族放在同一个工厂下生产。
代码实例
抽象产品类
首先为枪支和子弹分别定义抽象产品类,其中所有武器的开火与子弹装配方法内容基本一致,区别仅在于对子弹的校验。所以子类仅实现checkBulletType校验子弹类型的方法。而子弹类仅需传递口径长度即可。
/**
* 抽象武器类
* @author cjl
*/
public abstract class IWeapon {
//子弹
private IBullet bullet;
/**
* 校验子弹类型
*/
public abstract boolean checkBulletType(IBullet bullet);
/**
* 开火
*/
public void fire(){
if(checkBulletNotEmpty() && checkBulletType(getBullet()))
System.out.println(String.format("%s发射,发射的子弹口径为%.2f",
this.getClass().getSimpleName(),bullet.getDiameter()));
else
System.out.println(String.format("%s开枪失败!",this.getClass().getSimpleName()));
}
/**
* 校验是否有子弹
*/
public boolean checkBulletNotEmpty(){
return null == bullet?false:true;
}
/**
* 装配子弹
*/
public void togetherBullet(IBullet bullet){
if(bullet == null || !checkBulletType(bullet))
throw new RuntimeException("子弹装配异常");
this.bullet = bullet;
}
public IBullet getBullet() {
return bullet;
}
}
/**
* 抽象子弹类
* @author cjl
*/
public abstract class IBullet {
//子弹口径
private Float diameter;
public Float getDiameter() {
return diameter;
}
public void setDiameter(Float diameter) {
this.diameter = diameter;
}
}
具体产品类
/**
* AK47枪支类
*/
public class AK47 extends IWeapon{
@Override
public boolean checkBulletType(IBullet bullet) {
return AK47Bullet.class.equals(bullet.getClass())?true:false;
}
}
/**
* M16A4武器类
*/
public class M16A4 extends IWeapon{
@Override
public boolean checkBulletType(IBullet bullet) {
return M16A4Bullet.class.equals(bullet.getClass())?true:false;
}
}
/**
* AK47子弹类
*/
public class AK47Bullet extends IBullet{
{
super.setDiameter(7.62f);
}
}
/**
* M16A4子弹
*/
public class M16A4Bullet extends IBullet{
{
super.setDiameter(5.56f);
}
}
抽象工厂类
抽象工厂类主要提供生产产品的接口方法也就是生产枪支和子弹两种方法。
/**
* 抽象工厂类
* @author cjl
*/
public interface AbstractFactory {
/**
*生产枪支
*/
IWeapon createWeapon();
/**
*生产子弹
*/
IBullet createIBullet();
}
具体工厂类
现在的需求仅需要实现AK47和M16A4两种武器的生产,所以仅需要两个具体工厂类即可。
**
* AK47工厂
*/
public class AK47Factory implements AbstractFactory{
@Override
public IWeapon createWeapon() {
return new AK47();
}
@Override
public IBullet createIBullet() {
return new AK47Bullet();
}
}
/**
* M16A4工厂
*/
public class M16A4Factory implements AbstractFactory{
@Override
public IWeapon createWeapon() {
return new M16A4();
}
@Override
public IBullet createIBullet() {
return new M16A4Bullet();
}
}
运行代码
public static void main(String[] args) {
//获得具体工厂 这里用的是M16A4
AbstractFactory factory = new M16A4Factory();
//通过具体工厂生产枪支
IWeapon weapon = factory.createWeapon();
//通过具体工厂生产子弹并装配进枪支中
weapon.togetherBullet(factory.createIBullet());
//开枪!
weapon.fire();
}
运行结果:M16A4发射,发射的子弹口径为5.56
现在若将需求改变为使用AK47,此时仅需要将具体的工厂改为new AK47Factory()即可;
思考
根据上述例子,若需求增加多一个VSS狙击枪时需要怎么改?如果产品族中再增加一个扩容弹匣又需要怎么改???