学习了一段时间设计模式,就想分享一下自己的理解, 欢迎大家多多指点,指出不足之处哈
个人理解,工厂模式用于处理 如何获取实例对象 问题,建造者模式用于处理如何建造实例对象 问题(好像是废话。。。)。两者应该可以结合起来,下面将以商店售卖手机这场景来描述。
工厂模式:
简单工厂模式-工厂方法模式:
简单工厂模式由SimplePhoneFactory,集中获取不同的手机实例对象。
工厂方法模式由PhoneFactory的两个实现,分别获取不同的手机实例对象。
下面贴上代码,主要关注工厂类。
public abstract class Phone { /** * 品牌 */ protected String brand; /** * 操作系统 */ protected String os; /** * 充电 */ public abstract void charge(); //GET SET方法... } public class ApplePhone extends Phone { @Override public void charge() { System.out.println("普通充电"); } } public class SonyPhone extends Phone { @Override public void charge() { System.out.println("快充"); } } public class SimplePhoneFactory { public static Phone getPhone(int brand) { if (brand == 0) { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } } public interface PhoneFactory { Phone getPhone(); } public class SonyPhoneFactory implements PhoneFactory { public SonyPhone getPhone() { SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } } public class ApplePhoneFactory implements PhoneFactory { public ApplePhone getPhone() { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } } public class StoreA { private int brand; public StoreA(int brand) { super(); this.brand = brand; } /** * 补充手机 */ public void supplyPhone() { Phone phone = SimplePhoneFactory.getPhone(brand); // 补充手机逻辑... System.out.println("补充" + phone.getBrand() + "手机完成"); } public static void main(String[] args) { StoreA storeA = new StoreA(0); storeA.supplyPhone(); } } public class StoreB { private PhoneFactory phoneFactory; public StoreB(PhoneFactory phoneFactory) { super(); this.phoneFactory = phoneFactory; } /** * 补充手机 */ public void supplyPhone() { Phone phone = phoneFactory.getPhone(); // 补充手机逻辑... System.out.println("补充" + phone.getBrand() + "手机完成"); } public static void main(String[] args) { StoreB storeB = new StoreB(new SonyPhoneFactory()); storeB.supplyPhone(); } }
当要新增其它品牌的手机时,
简单工厂模式的SimplePhoneFactory类需要修改getPhone方法代码
工厂方法模式只需增加PhoneFactory的实现即可
小结:一类产品有多种不同实例对象(本例的手机,有不同品牌),当新增一种实例对象时(新增一个品牌的手机),工厂方法模式符合 对扩展开放,对修改封闭原则。
方法工厂模式-抽象工厂模式:增加一类产品:耳机,来方便对比。
工厂方法模式-建造者模式:接下来继续贴上代码,注意StoreC、StoreD的main方法。
public abstract class Headset { /** * 品牌 */ protected String brand; abstract void play(); public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } } public class AppleHeadset extends Headset { @Override void play() { // Apple 耳机播放逻辑 ... System.out.println("Apple 耳机播放完成"); } } public class SonyHeadset extends Headset { @Override void play() { // Sony 耳机播放逻辑... System.out.println("Sony 耳机播放完成"); } } public interface HeadsetFactory { Headset getHeadset(); } public class SonyHeadsetFactory implements HeadsetFactory { public SonyHeadset getHeadset() { SonyHeadset sonyHeadset = new SonyHeadset(); sonyHeadset.setBrand("Sony"); return sonyHeadset; } } public class AppleHeadsetFactory implements HeadsetFactory { public AppleHeadset getHeadset() { AppleHeadset appleHeadset = new AppleHeadset(); appleHeadset.setBrand("Apple"); return appleHeadset; } } public interface Factory { Phone getPhone(); Headset getHeadset(); } public class AppleFactory implements Factory { public ApplePhone getPhone() { ApplePhone applePhone = new ApplePhone(); applePhone.setBrand("Apple"); return applePhone; } public AppleHeadset getHeadset() { AppleHeadset appleHeadset = new AppleHeadset(); appleHeadset.setBrand("Apple"); return appleHeadset; } } public class SonyFactory implements Factory { public SonyPhone getPhone() { SonyPhone sonyPhone = new SonyPhone(); sonyPhone.setBrand("Sony"); return sonyPhone; } public SonyHeadset getHeadset() { SonyHeadset sonyHeadset = new SonyHeadset(); sonyHeadset.setBrand("Sony"); return sonyHeadset; } } public class StoreC { private PhoneFactory phoneFactory; private HeadsetFactory headsetFactory; public StoreC(PhoneFactory phoneFactory, HeadsetFactory headsetFactory) { super(); this.phoneFactory = phoneFactory; this.headsetFactory = headsetFactory; } /** * 补充手机 */ public void supplyPhone() { Phone phone = phoneFactory.getPhone(); // 补充手机逻辑... System.out.println("补充" + phone.getBrand() + "手机完成"); } /** * 补充耳机 */ public void supplyHeadset() { Headset headset = headsetFactory.getHeadset(); // 补充耳机逻辑... System.out.println("补充" + headset.getBrand() + "耳机完成"); } public static void main(String[] args) { StoreC storeD = new StoreC(new SonyPhoneFactory(), new SonyHeadsetFactory()); storeD.supplyPhone(); storeD.supplyHeadset(); } } public class StoreD { private Factory factory; public StoreD(Factory factory) { super(); this.factory = factory; } /** * 补充手机 */ public void supplyPhone() { Phone phone = factory.getPhone(); // 补充手机逻辑... System.out.println("补充" + phone.getBrand() + "手机完成"); } /** * 补充耳机 */ public void supplyHeadset() { Headset headset = factory.getHeadset(); // 补充耳机逻辑... System.out.println("补充" + headset.getBrand() + "耳机完成"); } public static void main(String[] args) { StoreD storeC = new StoreD(new SonyFactory()); storeC.supplyPhone(); storeC.supplyHeadset(); } }
从代码看,如果要从StoreC,StoreD中,选一个来实例化不同的专卖店,
抽象工厂中的StoreD更适合,只要在构造方法那传入不同的单个Factory,即可获得不同的专卖店。
而工厂方法中的StoreC,如果现在有耳机、手机、电脑、电视等等,那用StoreC实例化每个店,都得传入很多Factory,且得注意这些Factory都是同一品牌的。
小结:多类产品分别有多种不同实例对象(本例的手机,耳机都分别有不同品牌),而跨类别的实例对象有联系(本例的联系是同一品牌),暂且称有联系的那些实例对象为同一族。那抽象工厂模式可以让产品用户更方便获取同一族的产品。
疑问:如果产品用户没有获取同一族产品这需求,那工厂方法模式优点在哪?
我猜,如果不同族的产品线差异很大(如新增电脑,电视这两类产品,但Apple只有电脑,Sony只有电视),抽象工厂模式会产生很多无意义代码。
那抽象工厂模式的 AppleFactory的getTV,SonyFactory的getPC 就得返回null或者其它处理。
而工厂方法模式,只要按需新增ApplePCFactory,SonyTVFactory等。
上面两个类图好像似,完全看不出建造者模式的必要。再看看结合建造者模式的工厂方法模式的类图:
呃。。。这类图太复杂了,还不如直接对着代码说来得方便。主要关注那几行作了记号(<<<<<<<<<<)的代码。
public interface PhoneBuilder {
/**
* 刻铭牌
*/
void buildBrand();
/**
* 安装系统
*/
void buildOs();
Phone getResult();
}
public class ApplePhoneBuilder implements PhoneBuilder {
private ApplePhone applePhone = new ApplePhone();
public void buildBrand() {
applePhone.setBrand("Apple");
}
public void buildOs() {
applePhone.setOs("IOS");
}
public Phone getResult() {
return applePhone;
}
}
public class SonyPhoneBuilder implements PhoneBuilder {
private SonyPhone sonyPhone = new SonyPhone();
public void buildBrand() {
sonyPhone.setBrand("Sony");
}
public void buildOs() {
sonyPhone.setOs("Android");
}
public Phone getResult() {
return sonyPhone;
}
}
public interface PhoneFactory {
Phone getPhone();
}
//普通的工厂方法模式工厂类
public class SonyPhoneFactory implements PhoneFactory {
public SonyPhone getPhone() {
SonyPhone sonyPhone = new SonyPhone();
sonyPhone.setBrand("Sony");<<<<<<<<<<<<<<<<<<
sonyPhone.setOs("Android");<<<<<<<<<<<<<<<<<<
return sonyPhone;
}
}
public class ApplePhoneFactory implements PhoneFactory {
public ApplePhone getPhone() {
ApplePhone applePhone = new ApplePhone();
applePhone.setBrand("Apple");<<<<<<<<<<<<<<<<<<
applePhone.setOs("IOS");<<<<<<<<<<<<<<<<<<
return applePhone;
}
}
//结合建造者模式的工厂类
public class PhoneDirector {
public Phone construct(PhoneBuilder builder) {
builder.buildBrand();<<<<<<<<<<<<<<<<<<
builder.buildOs();<<<<<<<<<<<<<<<<<<
return builder.getResult();
}
}
public class ApplePhoneFactory implements PhoneFactory {
private PhoneDirector director = new PhoneDirector();
public ApplePhone getPhone() {
ApplePhoneBuilder builder = new ApplePhoneBuilder();
return (ApplePhone) director.construct(builder);
}
}
public class SonyPhoneFactory implements PhoneFactory {
private PhoneDirector director = new PhoneDirector();
public SonyPhone getPhone() {
SonyPhoneBuilder builder = new SonyPhoneBuilder();
return (SonyPhone) director.construct(builder);
}
}
普通的工厂方法模式中,同一类产品中不同实例对象的建造过程(SonyPhoneFactory,ApplePhoneFactory中作记号的代码),十分相似。那是不是可以抽出来,从而做到代码复用。
再看看结合了建造者模式的工厂类,就会明白建造者模式就是把建造过程抽到了导演类中(PhoneDirector中作记号的代码)。之所以能够这样,多亏了前面的多个Builder类。
又有疑问了,为了复用重复代码,却要新增那么多类,是不是有点得不偿失?
我猜,建造者模式抽取建造过程,不是为了减少代码量,而是为了把建造顺序统一到一个地方!以后一旦要修改建造顺序,也只需要修改导演类即可!
至于 抽象工厂模式-建造者模式,由于原理相似,就不哆嗦了。
小结:当一类产品中不同实例对象的建造过程相似,且未来很有可能要修改建造顺序的话,那建造者模式可以处理这种情况。
总结:从 普通的工厂方法模式 到 结合建造者模式的工厂方法模式,对产品用户(Store类)没有影响。所以对于产品用户来说,是否用建造者模式是不知情的,因为产品用户只关心产品的获取。所以正如开头所说,工厂模式用于处理 如何获取实例对象 问题,建造者模式用于处理 如何建造实例对象 问题。
下篇文章打算在 工厂方法模式-建造者模式 基础上,再结合 桥接模式,感觉这三个模式的结合运用,在实际中很常见。到时那类图想想都好恐怖。。。