工程模式是一个大家族,里面根据时间的推移和不断的优化,以及应用的场景,又分为不同的类别:
一:静态工厂模式
二:简单工厂模式
三:工厂方法模式
四:抽象工厂模式
一一举例。
静态工厂模式: 我们平常常用的静态工具类其实就是使用了静态工厂模式,类+静态方法,这种实现,例子:
public class StaticFactoryDemo {
public static boolean isTextEmpty(String text){
if(text == null || "".equals(text)){
return true;
}
return false;
}
}
简单工厂模式:声明一个类,专门用于根据不同参数创建不同的相关对象。举一个平时的例子,以及简单工厂是如何演变而来的。
假设KFC在卖汉堡,有三种汉堡可点,白羽鸡汉堡,田园鸡汉堡,香辣鸡汉堡,那么用代码的简单实现是怎么样的?
public class KFCStore {
//白羽鸡汉堡
public static final int TYPE_BAIYUJI = 1;
//田园鸡汉堡
public static final int TYPE_TIANYUANJI = 2;
//香辣鸡汉堡
public static final int TYPE_XIANGLAJI = 3;
public Hanmburger orderHamburger(int type){
Hanmburger hanmburger = null;
if(TYPE_BAIYUJI == type){
hanmburger = new Baiyuji();
}else if(TYPE_TIANYUANJI == type){
hanmburger = new Tianyuanji();
}else if(TYPE_XIANGLAJI == type){
hanmburger = new Xianglaji();
}
if(hanmburger != null){
hanmburger.cook();
}
return hanmburger;
}
}
class Hanmburger{
String name;
public Hanmburger(String name) {
this.name = name;
}
public void cook(){
System.out.println(name + "正在被煎炸..");
}
}
class Baiyuji extends Hanmburger{
public Baiyuji() {
this("白羽鸡");
}
public Baiyuji(String name) {
super(name);
}
}
class Tianyuanji extends Hanmburger{
public Tianyuanji() {
this("田园鸡");
}
public Tianyuanji(String name) {
super(name);
}
}
class Xianglaji extends Hanmburger{
public Xianglaji() {
this("香辣鸡");
}
public Xianglaji(String name) {
super(name);
}
}
分析:其实这样普通的实现对现有功能没有影响,完全可以。但是提一个问题,如果现在要加入虾堡呢?那么就需要去给order方法中,加入if判断,并返回。
带来的问题:
大忌:去直接修改以前的代码,这样会出现难以预期的错误。
第二:加入一个虾堡可以加一个if else,那么再加10种呢?其实这个方法只关注根据类型拿一个汉堡并且调用cook方法,所以我们可以将生产汉堡这个行为去交给一个“生产工厂”去负责。这个时候就演变来了简单工厂模式。
使用简单工厂优化后的代码:
建立工程类来完成创建汉堡的逻辑:
public class SimpleFactory {
//白羽鸡汉堡
public static final int TYPE_BAIYUJI = 1;
//田园鸡汉堡
public static final int TYPE_TIANYUANJI = 2;
//香辣鸡汉堡
public static final int TYPE_XIANGLAJI = 3;
/**
* 根据type获取汉堡
* @param type
* @return
*/
public Hanmburger getHanmburger(int type){
Hanmburger hanmburger = null;
if(TYPE_BAIYUJI == type){
hanmburger = new Baiyuji();
}else if(TYPE_TIANYUANJI == type){
hanmburger = new Tianyuanji();
}else if(TYPE_XIANGLAJI == type){
hanmburger = new Xianglaji();
}
return hanmburger;
}
}
修改KFCStore类代码:
public class KFCStore {
private SimpleFactory simpleFactory;
public KFCStore() {
/**
* 我喜欢讲简单工厂放入内部,因为外部调用的时候再让人家自己去new一个莫名其妙的工厂传入构造函数
* 总感觉会让调用者变得疑惑。
*/
simpleFactory =new SimpleFactory();
}
public Hanmburger orderHamburger(int type){
Hanmburger hanmburger = simpleFactory.getHanmburger(type);
if(hanmburger != null){
hanmburger.cook();
}
return hanmburger;
}
}
是的,这样就可以让order方法更关注自己所要做的事,而不用去写一大堆其实和他没关系的代码。
其实,简单工厂就是讲创建对象的业务逻辑封装到了一个类而已,所到底了就是解耦!分层!有人可能会觉得上面的优化除了代码变得清晰好看了,并没有多大的实际用处,增加一种类型的时候还是得去修改SimpleFactory类,没关系,后面还有简单工厂的更高级优化。
工厂方法模式:定义一个接口,用来创建对象,让子类去决定如何实例创建对象。
分析:对于简单工厂的应用我们可以看到,虽然让不想关的创建对象代码与逻辑封装到一个所谓的工程类中了,但是我们还是难以避免if,else的判断,并且增加类型的时候,要去改动工厂里的代码。上面还只是说了这一个KFC店去增加不同类的汉堡,那么如果我们需要增加一个店呢? 假如说要增加一个北京分店,北京分店里面除了提供基本的汉堡还要提供一种“老北京鸡肉卷”,再增加一个西安分店,要提供一种本地化的食品,名叫“肉夹馍”,那么如何做呢?用简单工厂的方法,那么只有去修改工厂的代码,增加两个if,else,这并没有多大妨碍,但是这个时候就需要注意了。北京分店其实是不需要判断type是否是肉夹馍的,以及西安分店不需要判断type是老北京鸡肉卷的,这种情况看似问题不大,但是在真正的系统中,让这样两个互相没关系判断耦合在一起是一个很危险的行为,假如说有人在西安店里面点了一个“老北京鸡肉卷”,这个店本来是没有的,那么却下单做出来一个,这是一个错误行为!这是不被允许的。
这个时候就需要具体的店面去决定他到底是如何来制定这个生成行为的,这个时候就需要工厂方法模式了,工厂方法模式是基于抽象的架构来做的。
上述代码的优化:
public abstract class KFCHeadStore {
public Hanmburger orderHamburger(int type){
Hanmburger hanmburger = getHanmburger(type);
if(hanmburger != null){
hanmburger.cook();
}
return hanmburger;
}
/**
* 具体子类去实现
* @param type
* @return
*/
protected abstract Hanmburger getHanmburger(int type);
}
抽象一个KFC总部,在订餐方法里做一些通用操作,比如煎炸,然后将获取哪种食物定义为一个抽象方法,供它的子类(分店)去实现。
北京分店:
/**
* 北京分店
* @author PC
*
*/
public class BeijingKFC extends KFCHeadStore{
/**
* 老北京鸡肉卷
*/
public static final int JIROUJUAN = 5;
protected Hanmburger getHanmburger(int type) {
if(JIROUJUAN == type){
return new Jiroujuan();
}
return null;
}
}
class Jiroujuan extends Hanmburger{
public Jiroujuan() {
this("老北京鸡肉卷");
}
public Jiroujuan(String name) {
super(name);
}
}
西安分店:
/**
* 北京分店
* @author PC
*
*/
public class BeijingKFC extends KFCHeadStore{
/**
* 老北京鸡肉卷
*/
public static final int JIROUJUAN = 5;
protected Hanmburger getHanmburger(int type) {
if(JIROUJUAN == type){
return new Jiroujuan();
}
return null;
}
}
class Jiroujuan extends Hanmburger{
public Jiroujuan() {
this("老北京鸡肉卷");
}
public Jiroujuan(String name) {
super(name);
}
}
这个时候就没有问题了,根据多态的写法
public class Test {
public static void main(String[] args) {
KFCHeadStore store = new BeijingKFC();
store.orderHamburger(BeijingKFC.JIROUJUAN);
//此方法没有反应的,安全的
store.orderHamburger(XianKFC.JIROUJUAN);
}
}
结果:
老北京鸡肉卷正在被煎炸..
啊哈哈哈哈哈,工厂方法模式结束。
四:抽象工厂模式
定义:为创建一组相关或者是相互依赖的对象提供一个借口,而不需要指定他们得具体类。
啥意思呢? 我们再来为上一个程序提出一个问题。假设汉堡这个东西是需要不同的原料来制作的,而我们作为一个全国连锁店,那必须制定原料成分啊(接口),然后规定制作流程(抽象工厂),这样就可以了,一个族群的抽象生成,就是抽象工厂模式。与工厂方法模式比较,就是一个产品需要有更多的未确定的互相依赖的产品来组成,那么就需要升级成抽象工厂模式。
上述实例的代码优化:
定义抽象产品与它的抽象组件:
/**
* 抽象产品
* @author PC
*
*/
abstract class Hanmburger{
String name;
public Hanmburger(Meet meet,Seasoning seasoning,String name) {
this.name = name;
System.out.println("使用了【"+meet.name+"】与【"+seasoning.name+"】制作了【"+this.name+"】");
}
public void cook(){
System.out.println("【"+this.name+"】正在被煎炸..");
}
}
/**
* 抽象组件类-肉
* @author Ade-rui
*
*/
abstract class Meet{
//关于抽象产品的具体方法
String name;
public Meet(String name) {
this.name = name;
}
}
/**
* 抽象组件类-调料
* @author Ade-rui
*
*/
abstract class Seasoning{
//关于抽象产品的具体方法
String name;
public Seasoning(String name) {
this.name = name;
}
}
定义抽象组件产品的工厂:
interface AbstractFactory{
public abstract Hanmburger getHanmburger();
}
原本的抽象KFC总店还是一样,只不过现在不是或许Hanmburger对象了,而是获取制作Hanmburger的工厂对象,再从工厂对象中获取组装Hanmburger对象
/**
* KFC抽象总部
* @author PC
*
*/
public abstract class KFCHeadStore {
public Hanmburger orderHamburger(int type){
AbstractFactory factory = getHanmburgerFactory(type);
Hanmburger hanmburger = factory.getHanmburger();
if(hanmburger!=null){
hanmburger.cook();
}
return hanmburger;
}
/**
* 具体子类去实现
* @param type
* @return
*/
protected abstract AbstractFactory getHanmburgerFactory(int type);
}
具体实现的产品类和组件类:
class BeijingJiroujuan extends Hanmburger{
public BeijingJiroujuan(Meet meet, Seasoning seasoning, String name) {
super(meet, seasoning, name);
}
}
/**
* 北京本地肉
* @author PC
*
*/
class BeijingMeet extends Meet{
public BeijingMeet(){
this("北京肉");
}
public BeijingMeet(String name) {
super(name);
}
}
/**
* 北京特色料
* @author PC
*
*/
class BeijingSeasoning extends Seasoning{
public BeijingSeasoning(){
this("北京料");
}
public BeijingSeasoning(String name) {
super(name);
}
}
具体定义组装老北京鸡肉卷的工厂:
public class BeijingFactory implements AbstractFactory {
public Hanmburger getHanmburger() {
Meet meet = new BeijingMeet();
Seasoning seasoning = new BeijingSeasoning();
return new BeijingJiroujuan(meet, seasoning, "老北京鸡肉卷");
}
}
测试:
public class Test {
public static void main(String[] args) {
KFCHeadStore store = new BeijingKFC();
store.orderHamburger(BeijingKFC.JIROUJUAN);
}
}
结果:
使用了【北京肉】与【北京料】制作了【老北京鸡肉卷】
【老北京鸡肉卷】正在被煎炸..
就这样没改动Client端调用代码,就将返回简单的对象,改成了返回组装的对象了。