System.out.println(“IPhone phone call…”);
}
}
3.3.3 简单工厂代码示例
客户端在调用简单工厂生产所需产品实例时,需要入参用于区分实例化哪个具体的产品。如下展示的枚举方式:
package com.liziba.pattern.factory.simpleFactory;
import com.liziba.pattern.factory.IMobilePhone;
import com.liziba.pattern.factory.HuaweiPhone;
import com.liziba.pattern.factory.IPhone;
import com.liziba.pattern.factory.XiaomiPhone;
/**
-
-
手机简单工厂
-
@Author: Liziba
-
@Date: 2021/6/28 21:14
*/
public class MobilePhoneFactory {
public static IMobilePhone getMobilePhone(PhoneTypeEnum phoneType) {
IMobilePhone phone = null;
switch (phoneType) {
case HUAWEI:
phone = new HuaweiPhone();
break;
case XIAOMI:
phone = new XiaomiPhone();
break;
case IPHONE:
phone = new IPhone();
break;
default:
break;
}
return phone;
}
/**
-
枚举类用于区分手机类型,本应写出去,为了减少类的示例个数内置于简单工厂中
-
也可以用字符串或者其他常量代替均可(但是我不推荐这种)
*/
enum PhoneTypeEnum {
HUAWEI(“华为”, “A”),
XIAOMI(“小米”, “B”),
IPHONE(“苹果”, “C”);
private String name;
private String value;
PhoneTypeEnum(String name, String value) {
this.name = name;
this.value = value;
}
}
}
如果觉得枚举导致类过多,也建议使用泛型来约束可入参的范围,同时使得简单工厂正确的实例化指定的产品。这种代码的写法非常简洁,其示例代码如下:
package com.liziba.pattern.factory.simpleFactory;
import com.liziba.pattern.factory.IMobilePhone;
/**
-
-
反射简单工厂示例代码
-
@Author: Liziba
*/
public class MobilePhoneFactory {
public static IMobilePhone getMobilePhone(Class<? extends IMobilePhone> clz) {
IMobilePhone phone = null;
if (null != clz) {
try {
phone = clz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return phone;
}
}
3.3.4 客户端调用示例
package com.liziba.pattern.factory.simpleFactory;
import com.liziba.pattern.factory.IMobilePhone;
/**
-
-
简单工厂测试
-
@Author: Liziba
*/
public class Test {
public static void main(String[] args) {
// 华为手机
IMobilePhone phone = MobilePhoneFactory.getMobilePhone(MobilePhoneFactory.PhoneTypeEnum.HUAWEI);
phone.sendShortMessage();
phone.call();
// 小米手机
phone = MobilePhoneFactory.getMobilePhone(MobilePhoneFactory.PhoneTypeEnum.XIAOMI);
phone.sendShortMessage();
phone.call();
// 苹果手机
phone = MobilePhoneFactory.getMobilePhone(MobilePhoneFactory.PhoneTypeEnum.IPHONE);
phone.sendShortMessage();
phone.call();
}
}
查看测试输出:
3.4 源码中的使用
简单工厂在源码中的使用十分广泛,例如我们常用的日历类:java.util.Calendar,Calender根据入参Locale时区来获取UnicodeLocaleType从而实例化对应的Calendar返回给客户端。
public static Calendar getInstance(TimeZone zone, Locale aLocale){
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone,Locale aLocale) {
// prev …
Calendar cal = null;
// 简单工厂模式使用场景
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType(“ca”);
if (caltype != null) {
switch (caltype) {
case “buddhist”:
cal = new BuddhistCalendar(zone, aLocale);
break;
case “japanese”:
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case “gregory”:
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
// post …
return cal;
}
3.5 优缺点总结
简单工厂模式优点
-
代码编码简单
-
调用方清晰明了
-
在一定程度上区分了产品和生产产品工厂之间的职责
简单工厂模式缺点
-
工厂职责不单一,能创建各种产品,理论上不符合单一职责原则(当然这个单一职责的职责区分界限视情况而定)
-
工厂代码耦合,新增产品会导致代码的修改,理论上不符合开闭原则。新增产品需要修改简单工厂类。这点符合前面说的,简单工厂模式在产品少或者产品个数能确定的场景使用最佳。
4、工厂方法
4.1 说明
工厂方法设计模式(Factory Method)也为虚拟构造函数(Virtual Constructor),我觉得老外这个Virtual Constructor称呼还挺有那个意思的;工厂通过实现一个统一个工厂接口,来约定生成何种产品。工厂方法将具体产品的生成推迟到了子类中,本身只约束不生产。这种方式解决了简单工厂不符合开闭原则的缺点。
工厂方法(Factory Method)其原文定义如下:
Define an interface for creating an object, but let subclassed decide which class to instantiate. Factory Method lets a class defer instantiation to subclassed.
工厂方法UML图示
4.2 使用场景
工厂方法由于其符合,开闭原则,在产品(实现类)个数不确定的情况下,使用该场景代码的可用性更强,其主要使用场景如下:
-
产品(类)无法预测其具体实现或实现的个数(类的个数多)
-
具体实现需要交给子类处理,父类只提供约束规范(实现很多,后期可能会一直加,或者当前不能全部穷举)
-
客户端的调用对于产品的创建(对象的实例化)可以透明,无需知道具体细节
4.3 使用举例
在简单工厂中移动手机接口IMobilePhone及其实现类华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone沿用,下面代码将不在重复演示,其创建了如下几个接口与类的层级关系:
-
移动手机接口IMobilePhone
-
华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone分别实现了IMobilePhone接口,并重写其中的方法抽象
-
工厂方法接口IMakePhoneFactory,提供创建手机的抽象方法
-
华为手机工厂HuaweiPhoneFactory、小米手机工厂XiaomiPhoneFactory、苹果手机工厂IPhonePhoneFactory分别实现了IMakePhoneFactory接口,并重新了其中的抽象方法,分别创建各自的手机实例。
4.3.1 IMobilePhone、HuaweiPhone、XiaomiPhone、IPhone代码示例
// 示例代码与简单工厂模式中的这几个类完全一致
4.3.2 工厂方法接口IMakePhoneFactory代码示例
IMakePhoneFactory只提供了一个makePhone()方法,用于子工厂实现并重写,子类中决定具体实例化的对象。
package com.liziba.pattern.factory.factoryMethod;
import com.liziba.pattern.factory.IMobilePhone;
/**
-
-
手机生产工厂接口
-
@Author: Liziba
*/
public interface IMakePhoneFactory {
/**
-
生产手机的方法定义
-
@return
*/
IMobilePhone makePhone();
}
4.3.3 工厂方法的三个实现子类代码示例
1、华为手机工厂实现类代码示例
package com.liziba.pattern.factory.factoryMethod;
import com.liziba.pattern.factory.IMobilePhone;
import com.liziba.pattern.factory.HuaweiPhone;
/**
-
-
华为手机生产工厂
-
@Author: Liziba
*/
public class HuaweiPhoneFactory implements IMakePhoneFactory{
@Override
public IMobilePhone makePhone() {
return new HuaweiPhone();
}
}
2、小米手机工厂实现类代码示例
package com.liziba.pattern.factory.factoryMethod;
import com.liziba.pattern.factory.IMobilePhone;
import com.liziba.pattern.factory.XiaomiPhone;
/**
-
-
小米手机生产工厂
-
@Author: Liziba
*/
public class XiaomiPhoneFactory implements IMakePhoneFactory{
@Override
public IMobilePhone makePhone() {
return new XiaomiPhone();
}
}
3、苹果手机工厂实现类代码示例
package com.liziba.pattern.factory.factoryMethod;
import com.liziba.pattern.factory.IMobilePhone;
import com.liziba.pattern.factory.IPhone;
/**
-
-
苹果手机生产工厂
-
@Author: Liziba
*/
public class IPhonePhoneFactory implements IMakePhoneFactory{
@Override
public IMobilePhone makePhone() {
return new IPhone();
}
}
4.3.4 客户端调用示例
客户端在获取指定产品时,只需要通过指定的产品的工厂获取即可,输出结果不再查看。
public class Test {
public static void main(String[] args) {
// 华为手机工厂,生产华为手机
IMobilePhone phone = new HuaweiPhoneFactory().makePhone();
phone.call();
// 小米手机工厂,生产小米手机
phone = new XiaomiPhoneFactory().makePhone();
phone.call();
}
}
4.4 源码中的使用
我们先引入maven依赖
org.slf4j
slf4j-api
1.7.30
在源码org.slf4j.ILoggerFactory可以查看其两个实现类,类关系图和部分相关代码在下面展示:
/**
- ILoggerFactory
*/
public interface ILoggerFactory {
Logger getLogger(String var1);
}
/**
- NOPLoggerFactory
*/
public class NOPLoggerFactory implements ILoggerFactory {
public NOPLoggerFactory() {
}
public Logger getLogger(String name) {
return NOPLogger.NOP_LOGGER;
}
}
/**
- SubstituteLoggerFactory
*/
public class SubstituteLoggerFactory implements ILoggerFactory {
// …
public synchronized Logger getLogger(String name) {
SubstituteLogger logger = (SubstituteLogger)this.loggers.get(name);
if (logger == null) {
logger = new SubstituteLogger(name, this.eventQueue, this.postInitialization);
this.loggers.put(name, logger);
}
return logger;
}
// …
}
4.5 优缺点总结
工厂方法模式优点
-
一个产品对应一个工厂,符合单一职责原则
-
易于扩展,新增产品只需新增一个工厂和相关产品即可,无需改动以前代码,符合开闭原则
-
屏蔽对象创建细节,客户端调用清晰
工厂方法模式缺点
-
产品数目过多时,会导致类的个数成倍增长
-
抽象程度高,难以理解,对开发开发要求较高
-
工厂方法在某些需要一个工厂生产多种产品的情况下显得乏力
5、抽象工厂
5.1 说明
前面有说道,简单工厂就像民间个体作坊,工厂方法就像小型加工厂,而抽象工厂就像大型代工厂。抽象工厂能解决工厂方法中一个工厂无法生产多个产品的问题。
抽象工厂(Abstract Factory),简单来说就是一个工厂的工厂,它将单个相关/依赖的工厂组合在一起而不指定它们的具体类的工厂。
抽象工厂(Abstract Factory)其原文定义如下:
Provide an interface for creating familiesof related or dependent objects without specifying their concrete classes.
抽象工厂UML图示
5.2 使用场景
在介绍抽象工厂的使用场景之前,我们先来介绍一个概念,产品族与产品等级结构。
我们知道华为、小米、苹果公司都不仅仅只生产手机;其也生产电脑、平板等各种产品。在上述描述的各种产品中,华为手机、小米手机与苹果手机就是一个产品等级,而华为手机、华为电脑、华为平板就构成了一个产品族。我们通过华为、小米和苹果的各种产品以一张图来示例这二者的关系:
在这张图中,五边形代表手机、圆形代表平板、三角形代表电脑;横坐标手机、平板、电脑分别代表三个不同的产品等级(例如橙色部分包含的三个三角形);纵坐标华为、小米、苹果各自的三种产品组合在一起称为产品族(例如绿色部分包含的一组五边形、原型和三角形)。清晰了这个概念我们就能大致的理解抽象工厂的使用场景也为后续举例加深映像做了铺垫。其使用场景如下:
-
系统需要配置多个系列的产品,并且它们约定一起使用(产品族)
-
只提供给客户端调用接口,不暴露具体实现
-
产品和产品族之间具有一定的一致性约束,通过接口规范来实现
5.3 使用举例
在简单工厂中移动手机接口IMobilePhone及其实现类华为手机HuaweiPhone、小米手机XiaomiPhone、苹果手机IPhone沿用,下面代码将不在重复演示,手机在这里是一个产品等级。此外新增电脑产品等级,其代码实现如下。
5.3.1 手机产品等级代码示例
// 示例代码与简单工厂模式中的这几个类完全一致
5.3.2 电脑产品等级代码示例
电脑接口ILaptop代码示例:
package com.liziba.pattern.factory;
/**
-
-
笔记本电脑接口
-
@Author: Liziba
*/
public interface ILaptop {
/**
- 敲代码,大家都爱编程
*/
void coding();
/**
- 打游戏,大家都爱打游戏
*/
void playGame();
}
ILaptop的三个实现类:
package com.liziba.pattern.factory;
/**
-
-
华为电脑
-
@Author: Liziba
*/
public class MagicBook implements ILaptop {
@Override
public void coding() {
System.out.println(“coding on MacBook…”);
}
@Override
public void playGame() {
System.out.println(“play game on MacBook…”);
}
}
package com.liziba.pattern.factory;
/**
-
-
小米电脑
-
@Author: Liziba
*/
public class XiaoMiPC implements ILaptop {
@Override
public void coding() {
System.out.println(“coding on MacBook…”);
}
@Override
public void playGame() {
System.out.println(“play game on MacBook…”);
}
}
package com.liziba.pattern.factory;
/**
-
-
苹果电脑
-
@Author: Liziba
*/
public class MacBook implements ILaptop {
@Override
public void coding() {
System.out.println(“coding on MacBook…”);
}
@Override
public void playGame() {
System.out.println(“play game on MacBook…”);
}
}
5.3.3 抽象工厂接口AbstractFactory示例代码
package com.liziba.pattern.factory.abstractFactory;
import com.liziba.pattern.factory.ILaptop;
import com.liziba.pattern.factory.IMobilePhone;
/**
-
-
抽象工厂
-
@Author: Liziba
*/
public interface AbstractFactory {
/**
-
生产笔记本电脑
-
@return
*/
更多学习和讨论,欢迎加入我们!
有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
这里有2000+小伙伴,让你的学习不寂寞~·
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
g on MacBook…");
}
@Override
public void playGame() {
System.out.println(“play game on MacBook…”);
}
}
package com.liziba.pattern.factory;
/**
-
-
苹果电脑
-
@Author: Liziba
*/
public class MacBook implements ILaptop {
@Override
public void coding() {
System.out.println(“coding on MacBook…”);
}
@Override
public void playGame() {
System.out.println(“play game on MacBook…”);
}
}
5.3.3 抽象工厂接口AbstractFactory示例代码
package com.liziba.pattern.factory.abstractFactory;
import com.liziba.pattern.factory.ILaptop;
import com.liziba.pattern.factory.IMobilePhone;
/**
-
-
抽象工厂
-
@Author: Liziba
*/
public interface AbstractFactory {
/**
-
生产笔记本电脑
-
@return
*/
[外链图片转存中…(img-z8D8BudX-1715353243464)]
更多学习和讨论,欢迎加入我们!
有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
这里有2000+小伙伴,让你的学习不寂寞~·
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!