这里写目录标题
工厂设计模式
需求:设计一个手机店卖手机系统
- 设计一个手机类,并定义两个子类,华为手机(HPhone),小米手机(MPhone);再设计一个卖手机店类(PhoneStore)具有卖手机的功能
- 具体类的设计:
不使用工厂模式实现
/**
* 抽象手机类
*/
public abstract class Phone {
public abstract String getBrand();
/**
* 打电话
*/
public void call() {
System.out.println("用" + getBrand() + "打电话");
}
/**
* 发短信
*/
public void seedMessage() {
System.out.println("用" + getBrand() + "发短信");
}
}
/**
* 华为手机 继承手机类
*/
public class HPhone extends Phone {
@Override
public String getBrand() {
return "华为手机";
}
}
/**
* 小米手机 继承手机类
*/
public class MPhone extends Phone {
@Override
public String getBrand() {
return "小米手机";
}
}
/**
* 手机店
*/
public class PhoneStore {
/**
* 下订单买手机
* @param brand 手机品牌
* @return phone
*/
public Phone orderPhone(String brand) {
// 声明phone的类型变量,根据不同类型创建不同类型的phone子类对象
Phone phone;
if ("华为".equals(brand)){
phone = new HPhone();
} else if ("小米".equals(brand)) {
phone = new MPhone();
}else{
throw new RuntimeException("对不起,没有这个品牌");
}
phone.call();
phone.seedMessage();
return phone;
}
}
public class Test01 {
public static void main(String[] args) {
// 创建手机店类
PhoneStore store = new PhoneStore();
// 选品牌购买
Phone phone = store.orderPhone("小米");
System.out.println(phone.getBrand());
}
}
存在的问题
- 后期如果想再增加或修改手机品牌那就要 修改 PhoneStore 类中的 orderPhone 方法,这样就违背了“开闭原则”——对修改关闭,对扩展开发
简单工厂模式(不是23种设计模式之一)
注意:简单工厂并不属于23种设计模式之一
结构
简单工厂包含如下角色:
- 抽象产品:定义了产品的规范,描述了产品的主要功能
- 具体产品:实现或继承了抽象产品的子类
- 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品
实现
- 使用简单工厂对上面案例进行改造,具体类图:
新增工厂类代码:
/**
* 简单手机工厂,专门用来生产各种手机
*/
public class SimplePhoneFactory {
public Phone createPhone(String brand){
Phone phone;
if ("华为".equals(brand)){
phone = new HPhone();
} else if ("小米".equals(brand)) {
phone = new MPhone();
}else{
throw new RuntimeException("对不起,没有这个品牌");
}
return phone;
}
}
修改手机店类
/**
* 手机店
*/
public class PhoneStore {
/**
* 下订单买手机
* @param brand 手机品牌
* @return phone
*/
public Phone orderPhone(String brand) {
// 创建手机店类
SimplePhoneFactory factory = new SimplePhoneFactory ();
Phone phone = factory.createPhone(brand);
// 测试手机功能
phone.call();
phone.seedMessage();
return phone;
}
}
测试
public class Test01 {
public static void main(String[] args) {
// 创建手机店类
PhoneStore phoneStore = new PhoneStore();
// 下单买手机
Phone phone = phoneStore.orderPhone("华为");
System.out.println(phone.getBrand());
}
}
结果
- 有了SimplePhoneFactory后,如果PhoneStore 类中的 orderPhone 需要 Phone 对象直接从工厂获取即可。解除了和 Phone 实现类的耦合,但是有产生了新的耦合,PhoneStore 对象和SimplePhoneFactory 的耦合。
如果后期添加新的手机,例如苹果手机,那又要修改 SimplePhoneFactory 的代码,违法了开闭原则。
优缺点:
-优点:封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新增产品直接修改工厂类,而不需要在原代码中修改。
- 缺点:新增产品时还是要修改工厂类的代码,违背了开闭原则
扩展
静态工厂(也不是23种之一)
- 开发中也有这样用的,好处是不用再创建工厂对象,直接通过类名调用方法即可
/**
* 简单手机工厂,专门用来生产各种手机
*/
public class SimplePhoneFactory {
public static Phone createPhone(String brand){
Phone phone;
if ("华为".equals(brand)){
phone = new HPhone();
} else if ("小米".equals(brand)) {
phone = new MPhone();
}else{
throw new RuntimeException("对不起,没有这个品牌");
}
return phone;
}
}
工厂方法模式(一个工厂生产一种类对象的模式)
使用工厂方法模式,是完全遵循开闭原则的
概念
定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。
工厂方法使一个产品的实例化延迟到其工厂的子类
结构
工厂方法模式的主要角色:
- 抽象工厂:提供了创建产品的接口,调用者通过访问具体的工厂的工厂方法来创建产品。
- 具体工厂:主要是实现抽象工厂中的抽象方法。完成具体产品的创建。
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品:实现了产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
实现
具体类图:
抽象手机类:
/**
* 手机抽象类
*/
public abstract class Phone {
public abstract String getName();
/**
* 发短信
*/
public void seedMessage(){
System.out.println("发短信");
}
/**
* 打电话
*/
public void call(){
System.out.println("打电话");
}
}
小米手机类
/**
* 小米手机类 继承 抽象手机类
*/
public class MPhone extends Phone{
/**
* 重写手机类的获取品牌方法
* @return String 小米手机
*/
@Override
public String getBrand() {
return "小米手机";
}
}
华为手机类
/**
* 华为手机类 继承 抽象手机类
*/
public class HPhone extends Phone{
/**
* 重写抽象手机类的获取品牌方法
* @return String 华为手机
*/
@Override
public String getBrand() {
return "华为手机";
}
}
抽象工厂:
/**
* 抽象手机工厂
*/
public interface PhoneFactory {
/**
* 创建手机的方法
* @return 手机
*/
Phone createPhone();
}
小米手机工厂:
/**
* 小米手机工厂 实现抽象手机工厂
*/
public class MPhoneFactory implements PhoneFactory{
/**
* 重写抽象手机工厂的手机生产方法,专门用来生产小米手机
* @return 小米手机
*/
@Override
public Phone createPhone() {
return new MPhone();
}
}
华为手机工厂
/**
* 华为手机工厂 实现抽象手机工厂
*/
public class HPhoneFactory implements PhoneFactory{
/**
* 重写抽象手机工厂的手机生产方法,专门生产华为手机
* @return 华为手机
*/
@Override
public Phone createPhone() {
return new HPhone();
}
}
手机店类
/**
* 手机店
*/
public class PhoneStore {
private PhoneFactory factory;
public void setFactory(PhoneFactory factory){
this.factory = factory;
}
/**
* 卖手机
*/
public Phone orderPhone(){
return factory.createPhone();
}
}
测试
public class Test01 {
public static void main(String[] args) {
// 创建手机店
PhoneStore store = new PhoneStore();
// 创建小米手机工厂对象
PhoneFactory mPhoneFactory = new MPhoneFactory();
store.setFactory(mPhoneFactory);
// 买手机
Phone phone = store.orderPhone();
System.out.println(phone.getBrand());
// 创建华为手机工厂对象
PhoneFactory hPhoneFactory = new HPhoneFactory();
store.setFactory(hPhoneFactory);
Phone phone1 = store.orderPhone();
System.out.println(phone1.getBrand());
}
}
结果
此时若是再新添加一种新的手机,例如苹果手机,只需要定义一个新 Phone 的子类 ApplePhone
并且再定义一个 PhoneFactory 的子实现类 ApplePhoneFactory,重写createPhone()方法即可,无需改动之前代码
到此已经满足了“开闭原则”——对修改关闭,对扩展开放
优缺点
- 优点:
- 用户只需要知道具体工厂的名称就可以得到所要的产品,无须知道产品的具体创建过程
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无需对原工厂进行任何修改。
- 缺点:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度。例如还要卖电脑就要新增一个ComputerFactory类(电脑工厂)
抽象工厂模式 (一个工厂可以产生多种类对象的模式)
工厂方法模式考虑的是同一等级下不同类型产品的生产,例如小米手机,华为手机,苹果手机等都是手机这一等级。
这些工厂只是生产同等级产品,但是很多工厂是综合型工厂能生产很多种不等级的产品。
例如:小米工厂不仅生产小米手机,还生产小米电脑,小米平板等等,平板,电脑,手机都是不同的等级,但都属于小米系产品
概念
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类别就能获得到同族的不同等级产品的产品模式
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,抽象工厂模式可以生产多个等级的产品。
结构
抽象工厂模式的主要角色:
- 抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
实现
现在有个电子产品店,店里除了卖手机还卖电脑,
小米手机,小米电脑, 华为手机,华为电脑,
如果按照之前的工厂方法模式,那么需要创建相对应的电脑类和相对应的电脑工厂。
但是小米的产品都是属于小米系(同族),华为的产品都是华为系(同族),并且不管是哪个品牌的手机是一个等级的(手机等级),不管那个品牌的电脑都是一个等级的(电脑等级)
所以可以尝试使用抽象类工厂模式实现。
类图如下:
抽象电脑类
/**
* 电脑抽象类
*/
public abstract class Computer {
public abstract String getBrand();
/**
* 玩游戏
*/
public void playGame() {
System.out.println("用" + getBrand() + "玩游戏");
}
/**
* 写代码
*/
public void coding() {
System.out.println("用" + getBrand() + "写代码");
}
}
小米电脑
/**
* 小米系电脑 继承抽象电脑类
*/
public class MComputer extends Computer{
/**
* 重新抽象电脑类的获取品牌方法
* @return String 小米电脑
*/
@Override
public String getBrand() {
return "小米系电脑";
}
}
华为电脑
/**
* 华为系电脑类 继承抽象电脑类
*/
public class HComputer extends Computer{
/**
* 重写抽象电脑类的获取品牌方法
* @return String 华为电脑
*/
@Override
public String getBrand() {
return "华为电脑";
}
}
抽象工厂
/**
* 电子厂抽象工厂 可以生产电脑,手机
*/
public interface ElectronicsFactory {
/**
* 生产手机
*/
Phone createPhone();
/**
* 生产电脑
*/
Computer createComputer();
}
小米工厂
/**
* 小米电子工厂 实现抽象电子抽象工厂
*/
public class MFactoryImpl implements ElectronicsFactory {
/**
* 重写抽象工厂的手机生产方法,专门生产小米手机
* @return 小米手机
*/
@Override
public Phone createPhone() {
return new MPhone();
}
/**
* 重写抽象工厂的电脑生产方法,专门生产小米电脑
* @return 小米电脑
*/
@Override
public Computer createComputer() {
return new MComputer();
}
}
华为工厂
/**
* 华为工厂 实现抽象电子工厂
*/
public class HFactoryImpl implements ElectronicsFactory{
/**
* 重写抽象电子厂的手机生产方法,专门生产华为手机
* @return 华为手机
*/
@Override
public Phone createPhone() {
return new HPhone();
}
/**
* 重写抽象电子厂的电脑生产方法,专门生产华为电脑
* @return 华为电脑
*/
@Override
public Computer createComputer() {
return new HComputer();
}
}
测试
public class Test01 {
public static void main(String[] args) {
// 创建小米工厂对象
MFactoryImpl factory = new MFactoryImpl();
// 创建华为工厂对象
// HFactoryImpl factory = new HFactoryImpl();
Computer computer = factory.createComputer();
Phone phone = factory.createPhone();
System.out.println(computer.getBrand());
computer.coding();
computer.playGame();
System.out.println(phone.getBrand());
phone.call();
phone.seedMessage();
}
}
结果
如果要新增加一个苹果系列产品的话,只需要再加一个对应的工厂类即可,不需要修改其他类
例如新增苹果电脑,苹果手机,只需要创建 AppleFactoryImp 实现 ElectronicsFactory类,重写createComputer和createPhone方法即可
优缺点
- 优点:
- 当一个品牌产品的多个对象被设计成一起工作时,它能保证客户端始终只使用一个品牌的产品中的对象
- 缺点:
- 当品牌中需要添加一个新的产品时,所有的工厂类都要修改。例如添加平板,那么每个工厂都要改变
使用场景
- 当需要创建的对象是一系列相关联或相互依赖的同品牌产品时,如小米手机,小米电脑,小米电视,小米平板等
- 系统中有多个品牌的产品,但是每次只使用其中某一个品牌的产品,例如使用华为电脑和华为手机。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品示例的创建细节和内部结构。
模式扩展 (简单工厂 + 配置文件)
可以通过工厂模式+配置文件的方式接触工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
第一步:定义配置文件 bean.properties
MPhone=com.creatorMode.factoryMode.configFactory.MPhone
HPhone=com.creatorMode.factoryMode.configFactory.HPhone
第二步:修改手机工厂
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
public class PhoneFactory {
// 1.定义容器对象存储手机对象
private static HashMap<String, Phone> map = new HashMap<>();
// 2.加载配置配件,获取配置文件中的全类名,并创建该类的对象进行存储,只需加载一次
static {
try {
// 创建properties对象
Properties properties = new Properties();
// 调用properties.load方法进行配置文件的加载
InputStream resourceAsStream = PhoneFactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties.load(resourceAsStream);
// 从properties中获取全类名并创建对象
for (Object o : properties.keySet()) {
String className = properties.getProperty((String) o);
// 通过反射创建对象
Class aClass = Class.forName(className);
Phone phone = (Phone) aClass.newInstance();
// 将名称和对象存储到容器中
map.put((String) o, phone);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 根据名称获取对象
* @return phone
*/
public static Phone createPhone(String name) {
return map.get(name);
}
}