前言
在平时编程中,我们往往使用“new”关键字在创建一个对象,这样简单方便而且直接。自己个人项目或者小型开发中这么做是没有很大的问题的,编码效率高而且不会过度设计。但是,当一个项目涉及到的类有成百上千的时候,这么做的后果简直是灾难级的:试想一下,一个类在上百个方法中被new出来,为此需要消耗的内存暂且不说,就说这个类如果被弃用了,将使用另一个设计更完善的类作为替代,你该怎么办?你不知道这些对象在哪里被创建了,删掉影不影响逻辑实现,甚至可能系统停摆,你能怎么办?直接在原本的类上进行修改当然是可以的,但不符合开闭原则,不应当提倡。
在面向对象编程领域中,开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。该特性在产品化的环境中是特别有价值的,在这种环境中,改变源代码需要代码审查,单元测试以及诸如此类的用以确保产品使用质量的过程。遵循这种原则的代码在扩展时并不发生改变,因此无需上述的过程。 — 百度百科
每当我们使用一次new,当前对象与new出来的对象耦合度就会增加,同样不符合OO(object-oriented 面向对象)的原则。
这时,我们可以考虑使用某些设计模式来解决这些问题。在介绍设计模式之前,首先强调三点:
合适 ,合适 ,还是合适。不要将生搬硬套,设计模式也有缺点,无脑使用设计模式只会设计过度,事倍功半。要根据具体的场景来使用合适的设计模式,才能真正发挥出设计模式的优势。如果这类只使用一次两次,那么放心大胆地"new"吧。
一,简单工厂模式
工厂,顾名思义,就是生产对象的对方。当我们需要创建某一个对象时,就使用工厂,让它返回一个对象,而不是自己new一个。为什么叫简单工厂呢,这个稍后再讲。
简单工厂模式,又称静态工厂方法模式(Static Factory Method,但它并不属于那二十三中设计模式)。通过定义一个专门的类,由这个专门的类的静态方法来创建其他类的对象------通常这些类有共同的父类。
举个栗子:
我想要卖华为和小米两个品牌的手机,一般情况我们首先创建这两个品牌的类,设计一些方法,然后new创建对象之后调用对应方法即可。但我们这里使用简单工厂模式。
首先创建一个手机接口,然后创建两个实现类实现手机接口的方法。这样工厂需要提供的产品就准备好了。
interface Phone {
void sell();
}
/** 手机(华为)*/
class HuaWei implements Phone{
/** 卖手机 */
@Override
public void sell() {
System.out.println("华为");
}
}
/** 手机(小米) */
class XiaoMi implements Phone{
/** 卖手机 */
@Override
public void sell() {
System.out.println("小米");
}
}
之后建简单工厂。
/** 手机工厂*/
class PhoneFactory {
/**
* 创建手机对象
* @param type 手机的品牌
* @return 具体的手机对象
*/
public static Phone create(String type) {
switch (type) {
case "HuWei":
return new HuaWei();
case "XiaoMi":
return new XiaoMi();
default:
return null;
}
}
}
就是这么朴实无华,需要什么对象,通过switch判断输出对象就行。后续还要扩展添加对象,直接再case后面加。
/** 手机工厂*/
class PhoneFactory {
/**
* 创建手机对象
* @param type 手机的品牌
* @return 具体的手机对象
*/
public static Phone create(String type) {
switch (type) {
case "HuWei":
return new HuaWei();
case "XiaoMi":
return new XiaoMi();
/** 后续工厂还提供苹果手机 */
case "apple":
returen new Apple();
default:
return null;
}
}
}
接下就是使用了:
/** 创建手机工厂 */
PhoneFactory phoneFactory = new PhoneFactory();
/** 创建华为手机对象 */
Phone huwei = phoneFactory.create("HuWei");
/** 创建小米手机对象 */
Phone xiaomi = phoneFactory.create("XiaoMi");
/** 出售 */
huwei.sell();
xiaomi.sell();
是不是很简单?所以才叫简单工厂嘛。对象和工厂设计好后,后续需要添加新的对象,无需变更其他源码,直接在简单工厂中的判定逻辑中添加后续即可。
从上面的代码示例来看,我们可以明显发现简单工厂的几个特点:
- 使用的对象最好时具有多态的对象。
- 客户无需知道具体对象的类目,只需要向简单工厂提供参数。
- 创建工厂的参数,无需将代码写死,可以通过引入配置文件的字段来更换和添加新的对象,提高了系统的灵活性。
除此之外,简单工厂的缺点也很明显:
- 工厂类集中了所有产品的创建逻辑,职责过重,一旦出现bug,整个系统都会收到影响。
- 简单工厂使用静态工厂方法,无法形成基于继承的等级结构。
- 系统扩展困难。虽然上个例子中,添加新的对象就是在switch后添加新的case,但实际开发场景比这复杂的多,比如在工厂中创建对象时需要先判断客户权限,根据权限创建不同的登录对象时,逻辑比较复杂,添加一个新的对象,可能需要改动原有的逻辑,这就增大了风险。
二,工厂模式
先解释一下简单工厂与工厂模式的区别,上面的简单工厂,能创建华为小米手机对象,也能创建衣服裤子对象,可以创建一切你预先设置好的对象。但实际情况并不是这么的,拖拉机厂经过改造能开出坦克但绝对开不出飞机航母。现实是工厂并不能生产所有东西,华为手机厂只生产华为手机,小米手机厂只生产小米手机。而工厂模式,就是专门的工厂生产特定东西。
举个栗子:
产品仍然不变
/** 手机接口 */
interface Phone {
void sell();
}
/** 手机(华为)*/
class HuaWei implements Phone{
/** 卖手机 */
@Override
public void sell() {
System.out.println("华为");
}
}
/** 手机(小米) */
class XiaoMi implements Phone{
/** 卖手机 */
@Override
public void sell() {
System.out.println("小米");
}
}
然后,创建一个抽象工厂类,创建两个实现类来继承这个抽象工厂
/** 基类工厂 */
abstract class ChinaPhoneFactory {
abstract Phone createPhone();
}
/** 华为手机工厂 */
class HuaWeiFactory extends ChinaPhoneFactory {
@Override
public Phone createPhone() {
return new HuaWei();
}
}
/** 小米手机工厂 */
class XiaoMiFactory extends ChinaPhoneFactory {
@Override
public Phone createPhone() {
return new XiaoMi();
}
}
华为手机工厂实现了父类的抽象方法,返回一个华为手机对象,小米手机工厂也是如此。
使用方法:与简单工厂方法不同,我们要先创建专门的工厂,然后通过这些工厂生产特定的产品。
/** 创建华为工厂 */
ChinaPhoneFactory huaWeiFactory = new HuaWeiFactory();
/** 创建小米工厂 */
ChinaPhoneFactory xiaoMiFactory = new XiaoMiFactory();
/** 创建华为手机对象*/
Phone huWei = huaWeiFactory.createPhone();
/** 创建小米手机对象 */
Phone xiaoMi = xiaoMiFactory.createPhone();
/** 出售 */
huWei.sell();
xiaoMi.sell();
工厂模式与简单工厂模式在结构上的不同不是很明显。简单工厂模式把核心放在具体类上,而工厂方法类的核心是抽象工厂类,将让实现这个抽象类的类去实例化对象,就是将类的实例话推迟到实现子类中进行。
当系统扩展需要添加新的对象时,仅仅需要添加一个具体对象和一个具体工厂对象,原有工厂对象不需要进行任何修改,很好地符合了开闭原则。而简单工厂模式在添加新的对象后不得不修改工厂方法,扩展性不好。
工厂模式退化后可以演变成简单工厂模式所以才叫简单工厂 。