什么是工厂模式
工厂模式:通过建造工厂,让产品使用与生产过程解耦。
在GoF23种设计模式中,属于创建型模式( Creational patterns)。
工厂模式一般有3种形式。
- 简单工厂
- 工厂方法
- 抽象工厂
这三种形式其实就是工厂模式的演进。本质上没有任何区别。就是提供一个公共的接口,把具体实现交给子类,增加程序拓展性,降低耦合。
简单工厂
简单工厂又叫静态工厂方法。
- UML
- code
Phone.java
public interface Phone {
void show();
}
HuaWeiPhone.java
public class HuaWeiPhone implements Phone{
@Override
public void show() {
System.out.println("This is a HuaWei Phone !");
}
}
ApplePhone.java
public class ApplePhone implements Phone{
@Override
public void show() {
System.out.println("This is an Apple Phone !");
}
}
Factory.java
public class Factory {
public static Phone getPhone(String name) {
Phone phone = null;
if ("HuaWei".equals(name)) {
phone = new HuaWeiPhone();
}
if ("Apple".equals(name)) {
phone = new ApplePhone();
}
return phone;
}
}
Test.java
public class Test {
public static void main(String[] args) {
Phone phone;
phone = Factory.getPhone("HuaWei");
phone.show();
phone = Factory.getPhone("Apple");
phone.show();
}
}
output
This is a HuaWei Phone !
This is an Apple Phone !
- 用法
当产品类型不多时,且长时间不会改动时,就很适合用该种模式。如:org.slf4j.impl.Log4jLoggerFactory#getLogger。
public class Log4jLoggerFactory implements ILoggerFactory {
...
public Logger getLogger(String name) {
Logger slf4jLogger = (Logger)this.loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
if (name.equalsIgnoreCase("ROOT")) {
log4jLogger = LogManager.getRootLogger();
} else {
log4jLogger = LogManager.getLogger(name);
}
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
return (Logger)(oldInstance == null ? newInstance : oldInstance);
}
}
...
}
该方法传入name
参数,然后通过判断后返回Logger
对象。
因此,简单工厂在运用时,很普遍,且不一定严格采用上述格式命名,但理念确实是一致的。
- 优点
通过简单工厂模式,调用只用关心产品的使用,而屏蔽掉了产品的生产过程。实现代码解耦。
- 不足
违背开闭原则。以上述模型举例,业务新增小米手机业务时,我们需要在工厂类中新增判断逻辑,修改已经稳定的代码,这本身就是一种风险。实际业务开发中,老代码的重构,往往都是一件极为恶心的事情。
工厂方法
简单工厂是将将产品类的实例化操作延迟到工厂子类中完成。新增产品类型时,只需要实现产品和工厂基类,即可完成拓展。对已有代码无需修改。遵守开闭原则,对修改关闭对拓展打开。
- UML
- code
Factory.java
public interface Factory {
Phone getPhone();
}
HuaWeiFactory.java
public class HuaWeiFactory implements Factory {
@Override
public Phone getPhone() {
return new HuaWeiPhone();
}
}
AppleFactory.java
public class AppleFactory implements Factory {
@Override
public Phone getPhone() {
return new ApplePhone();
}
}
Phone.java
public interface Phone {
void show();
}
HuaWeiPhone.java
public class HuaWeiPhone implements Phone{
@Override
public void show() {
System.out.println("This is a HuaWei phone!");
}
}
ApplePhone.java
public class ApplePhone implements Phone {
@Override
public void show() {
System.out.println("This is an Apple Phone!");
}
}
Test.java
public class Test {
public static void main(String[] args) {
Factory factory;
Phone phone;
factory = new HuaWeiFactory();
phone = factory.getPhone();
phone.show();
factory = new AppleFactory();
phone = factory.getPhone();
phone.show();
}
}
output
This is a HuaWei phone!
This is an Apple Phone!
- 用法
当不能确定产品的具体个数时,创建产品工厂可以采用工厂方法模式。这样既不影响现有产品的生产,又能保留未来产品的拓展性。如:org.apache.dubbo.cache.CacheFactory
- 优势
遵守开闭原则,代码维护成本低。
- 不足
增加新的产品时,需要实现新的工厂基类和产品基类。容易类爆炸。
抽象工厂
抽象工厂生产组合型产品的工厂类。上面我们知道,工厂方法模式容易类爆炸。于是,我们想能不能将多个工厂方法模式进行抽象合并成一个工厂,同时生产多个产品。最终,抽象工厂模式来了。
- UML
- code
LapTop.java
public interface LapTop {
void show();
}
HuaWeiLapTop.java
public class HuaWeiLapTop implements LapTop {
@Override
public void show() {
System.out.println("This is a HuaWei LapTop!");
}
}
AppleLapTop.java
public class AppleLapTop implements LapTop {
@Override
public void show() {
System.out.println("This is an Apple LapTop!");
}
}
Phone.java
public interface Phone {
void show();
}
HuaWeiPhone.java
public class HuaWeiPhone implements Phone {
@Override
public void show() {
System.out.println("This is a HuaWei Phone!");
}
}
ApplePhone.java
public class ApplePhone implements Phone {
@Override
public void show() {
System.out.println("This is an Apple Phone!");
}
}
Factory.java
public interface Factory {
Phone getPhone();
LapTop getLapTop();
}
HuaWeiFactory.java
public class HuaWeiFactory implements Factory {
@Override
public Phone getPhone() {
return new HuaWeiPhone();
}
@Override
public LapTop getLapTop() {
return new HuaWeiLapTop();
}
}
AppleFactory.java
public class AppleFactory implements Factory {
@Override
public Phone getPhone() {
return new ApplePhone();
}
@Override
public LapTop getLapTop() {
return new AppleLapTop();
}
}
Test.java
public class Test {
public static void main(String[] args) {
Factory factory;
Phone phone;
LapTop lapTop;
factory = new HuaWeiFactory();
phone = factory.getPhone();
lapTop = factory.getLapTop();
phone.show();
lapTop.show();
factory = new AppleFactory();
phone = factory.getPhone();
lapTop = factory.getLapTop();
phone.show();
lapTop.show();
}
}
output
This is a HuaWei Phone!
This is a HuaWei LapTop!
This is an Apple Phone!
This is an Apple LapTop!
- 用法
通常当多个产品之间有组合关系时,我们就可以用抽象工厂模式来提供统一的接口输出。
比如说上面的例子中,无论是手机或是电脑都可以归入电子产品类,无论是华为或者苹果都有能力进行生产。故就可以建立一个电子设备工厂,进行统一生产,即上述Factory.java
。
但是请注意,如果这时要加一个汽车的产品类,就不能用抽象工厂,因为汽车和手机、电脑关系性弱。华为、苹果目前为止也没见生产汽车,对吧 - -
实例如:org.springframework.beans.factory.BeanFactory
这个BeanFactory
可以getBean、getType、getAliases。 因为bean、type、aliases正是基本bean的组成部分。
-
优点
- 新增工厂很方便,符合开闭原则
- 产品组合输出,符合高内聚低耦合的实际目标
-
缺点
- 新增产品时,需要给所有实现工厂添加产品逻辑,违反了开闭原则。 如,现在要加一个手环设备。此时,华为工厂、苹果工厂都要实现手环的产品逻辑。
产品族与产品等级
上述举例中,我所说的产品和产品类型有个官方的名词。就是产品等级和产品族。
产品等级:其实就是产品的种类。几个等级,就是有几种产品。只不过名词这么叫。
产品族:族即为种族。不管你是老师、医生还是程序员,只要你姓赵。就都是赵氏是宗族的人,500年前是一家嘛。同理,无论是华为手机、电脑还是路由都是华为产的,那就都属于华为这一产品族。
产品等级就是产品,产品族就是多个产品的共性。
显然,工厂方法适合生产一种产品。而抽象工厂可以生产多个组合的产品。
总结
仅有一个产品等级下,当我们已经知道产品族的个数,且产品族长时间不会改变,就使用简单工厂。
仅有一个产品等级下,当我们不确定产品族的个数,需要保持其拓展性时,就是用工厂方法。
有多个和组合的产品等级下,我们可以使用抽象工厂。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名,转载请标明出处
最后编辑时间为:
2019-10-16