设计模式(一):简单工厂模式
简单工厂模式(Simple Factory Pattern)是工厂模式的一种简单实现方式,它属于创建型模式。简单工厂模式中只有一个“工厂”类,它可以根据类型来创建不同的“产品”实例。
也就是说,创建“产品”的实例,不再是通过直接new
的方式,而是通过“工厂”进行生成,客户端无需关心实例创建的细节。
在简单工厂模式中,创建“产品”实例的方法通常是静态方法,因此简单工厂模式又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单工厂模式并不在GoF 23种设计模式之列,有资料说它不是一种设计模式,而更像是一种编程习惯。但由于它逻辑简单,经常被使用,还是有必要深入了解一下的。
角色
通过上面的介绍可以看出,简单工厂模式主要有下面几种角色:
- 抽象产品(Product):它是所有具体产品的公共接口。在工厂类中的创建产品实例方法,应该返回此接口(面向接口编程)。
- 具体产品(ConcreteProduct):是工厂类中创建的对象。
- 工厂(Factory):它是简单工厂模式的核心类,负责实现所有产品实例的创建逻辑。它有一个通过类型来创建产品实例的方法,该方法通常是静态的。
- 客户端(Client):上面的工厂就是为客户端服务的,客户端调用工厂类中方法,来获取产品实例。
下面是该模式的UML类图:
实现
下面通过一个手机的案例,来讲解该模式的实现。
抽象产品接口:Phone
创建一个手机接口,它可以打电话和发短信。
/**
* 手机接口
*/
public interface Phone {
/**
* 打电话
*/
void call();
/**
* 发短信
*/
void sendSMS();
}
两个具体产品类:XiaomiPhone和RedmiPhone
为手机接口创建两个实现类,一个是小米手机,一个是红米手机,当然也可以有更多的实现类。
/**
* 小米手机实现类
*/
public class XiaomiPhone implements Phone {
@Override
public void call() {
System.out.println("使用小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("使用小米手机发短信");
}
}
/**
* 红米手机实现类
*/
public class RedmiPhone implements Phone {
@Override
public void call() {
System.out.println("使用红米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("使用红米手机发短信");
}
}
工厂类:PhoneFactory
创建一个手机工厂类,该类可以根据手机类型获取手机实例。
注:在通常情况下,这个类型应该使用枚举类来表示
/**
* 手机工厂类
*/
public class PhoneFactory {
/**
* 根据类型获取手机
*
* @param type
* @return
*/
public static Phone createPhone(String type) {
switch (type) {
case "xiaomi":
return new XiaomiPhone();
case "redmi":
return new RedmiPhone();
default:
return null;
}
}
}
客户端类:Client
创建一个客户端类,用于调用手机工厂类,来生成手机实例。
/**
* 客户端类
*/
public class Client {
public static void main(String[] args) {
System.out.println("================小米手机================");
Phone xiaomiPhone = PhoneFactory.createPhone("xiaomi");
if (xiaomiPhone != null) {
xiaomiPhone.call();
xiaomiPhone.sendSMS();
}
System.out.println("================红米手机================");
Phone redmiPhone = PhoneFactory.createPhone("redmi");
if (redmiPhone != null) {
redmiPhone.call();
redmiPhone.sendSMS();
}
}
}
运行结果
运行客户端main
方法,会打印如下内容:
================小米手机================
使用小米手机打电话
使用小米手机发短信
================红米手机================
使用红米手机打电话
使用红米手机发短信
优缺点
优点:
- 使对象的创建和使用分离,对象的创建交给专门的工厂类负责,客户端不需要关心是怎么创建的,只关心如何使用就行了。
- 只有一个工厂类,逻辑清晰,便于理解。
缺点:
- 工厂类不够灵活,每增加一个产品,就要修改工厂类,代码耦合性太高。
- 工厂类负责所有产品的创建,违反了单一职责
典型应用
java.util.Calendar
类是Java自带的日历抽象类,它是典型的简单工厂模式。
该类的角色,即是工厂,也是抽象产品。它有三个子类:BuddhistCalendar
、JapaneseImperialCalendar
和 GregorianCalendar
,这个三子类是具体产品角色。
Calendar
类有多个获取实例的重载方法getInstance()
,但最终都会调用createCalendar()
方法,来生成具体的实例。从下面代码片段也可以看出,它是根据不同的区域类型,来创建对应实例的。
// 获取默认时区和区域的日历实例
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
// 获取指定时区和默认区域的日历实例
public static Calendar getInstance(TimeZone zone)
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
// 获取默认时区和指定区域的日历实例
public static Calendar getInstance(Locale aLocale)
{
return createCalendar(TimeZone.getDefault(), aLocale);
}
// 获取指定时区和指定区域的日历实例
public static Calendar getInstance(TimeZone zone, Locale aLocale)
{
return createCalendar(zone, aLocale);
}
// 真正获取日历实例的方法
private static Calendar createCalendar(TimeZone zone, Locale aLocale)
{
// ......
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;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
完整代码
完整代码请访问我的Github,若对你有帮助,欢迎给个⭐,感谢~~🌹🌹🌹
- 点赞
- 收藏
- 关注作者
评论(0)