一、工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
二、介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
三、简单工厂模式实现
简单工厂模式(不属于GOF的23种经典设计模式)
简单工厂模式不是一种设计模式,反而比较像是一种编程习惯
1、结构
简单工厂包括如下角色:
①抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
②具体产品:实现或集成抽象产品类的子类
③具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品
2、具体实现
创建一个抽象Coffee类
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: Coffee
* @Description: 咖啡类
* @Date: 2022/4/1 11:15
**/
public abstract class Coffee {
public abstract String getName();
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("加奶");
}
}
创建两个具体的Coffee类
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: LatteCoffee
* @Description: 拿铁咖啡
* @Date: 2022/4/1 11:21
**/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: AmericanCoffee
* @Description: 美式咖啡
* @Date: 2022/4/1 11:22
**/
public class AmericanCoffee extends Coffee{
@Override
public String getName() {
return "美式咖啡";
}
}
生成Coffee工厂
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: SimpleCoffeeFactory
* @Description: 简单咖啡工厂
* @Date: 2022/4/1 11:23
**/
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type){
Coffee coffee=null;
if("american".equals(type)){
coffee=new AmericanCoffee();
}else if ("latte".equals(type)){
coffee=new LatteCoffee();
}else {
throw new RuntimeException("对不起,您点的咖啡没有");
}
return coffee;
}
}
生成Coffee商店
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: CoffeeStore
* @Description: Coffee商店
* @Date: 2022/4/1 11:28
**/
public class CoffeeStore {
public Coffee orderCoffee(String type){
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
// 调用生产咖啡的方法
Coffee coffee = factory.createCoffee(type);
// 咖啡增加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
客户购买Coffee
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: Client
* @Description: TODO
* @Date: 2022/4/1 11:30
**/
public class Client {
public static void main(String[] args) {
CoffeeStore coffeeStore = new CoffeeStore();
Coffee coffee = coffeeStore.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
运行结果
优点:
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在源代码种修改,这样就降低了客户代码修改的可能,更容易扩展开发
缺点:
增加新产品时还需要修改工厂类的代码。违背了“开闭原则”
四、静态工厂
在开发种也有一部分人将工厂类中的创建对象的功能定义为静态的,这就是静态工厂模式,它也不是23种设计模式种的
代码如下:只需要将创建对象的方法修改为静态方法
package FP_01_SimpleFactory;
/**
* @Author: {LZG}
* @ClassName: SimpleCoffeeFactory
* @Description: 简单咖啡工厂
* @Date: 2022/4/1 11:23
**/
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type){
Coffee coffee=null;
if("american".equals(type)){
coffee=new AmericanCoffee();
}else if ("latte".equals(type)){
coffee=new LatteCoffee();
}else {
throw new RuntimeException("对不起,您点的咖啡没有");
}
return coffee;
}
}
此时的Coffee商店
package FP_02_StaticFactory;
/**
* @Author: {LZG}
* @ClassName: CoffeeStore
* @Description: Coffee商店
* @Date: 2022/4/1 11:28
**/
public class CoffeeStore {
public Coffee orderCoffee(String type){
// 调用生产咖啡的方法
Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
// 咖啡增加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
五、 工厂方法模式
针对以上的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则
1、概念:
定义一个创建对象的接口,让子类决定用实例化用哪个产品对象。工厂方法使一个产品类的实例化延迟到其子类工厂。
2、结构
工厂方法模式的主要角色
①抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
②具体工厂(Concerte Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建
③抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能
④具体产品(Concerte Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
3、具体实现:
创建Coffee的抽象类
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: Coffee
* @Description: 咖啡类
* @Date: 2022/4/1 11:15
**/
public abstract class Coffee {
public abstract String getName();
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("加奶");
}
}
创建Coffee具体的实例对象
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: AmericanCoffee
* @Description: 美式咖啡
* @Date: 2022/4/1 11:22
**/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: LatteCoffee
* @Description: 拿铁咖啡
* @Date: 2022/4/1 11:21
**/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
创建 CoffeeFactory接口
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: CoffeeFactory
* @Description: Coffee工厂接口
* @Date: 2022/4/1 13:59
**/
public interface CoffeeFactory {
// 创建咖啡对象的方法
Coffee createCoffee();
}
实现CoffeeFactory接口生成对应Coffee对象的工厂
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: LatteCoffee
* @Description: 拿铁咖啡生产工厂
* @Date: 2022/4/1 11:21
**/
public class LatteCoffeeFactory implements CoffeeFactory {
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: AmericanCoffee
* @Description: 美式咖啡生产工厂
* @Date: 2022/4/1 11:22
**/
public class AmericanCoffeeFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
Coffee商店
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: CoffeeStore
* @Description: Coffee商店
* @Date: 2022/4/1 11:28
**/
public class CoffeeStore {
// 创建一个CoffeeFactory接口对象
private CoffeeFactory factory;
// 注入一个具体的Coffee实例对象工厂
public void setFactory(CoffeeFactory factory){
this.factory=factory;
}
public Coffee orderCoffee(){
// 调用接口方法生成对应的Coffee对象
Coffee coffee = factory.createCoffee();
// 加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
客户
package SP_03_MethodFactory;
/**
* @Author: {LZG}
* @ClassName: Client
* @Description: TODO
* @Date: 2022/4/1 14:06
**/
public class Client {
public static void main(String[] args) {
// 创佳咖啡店对象
CoffeeStore store=new CoffeeStore();
// 创建工厂对象
CoffeeFactory factory = new AmericanCoffeeFactory();
store.setFactory(factory);
// 点咖啡
Coffee coffee = store.orderCoffee();
System.out.println(coffee.getName());
}
}
测试结果
4、工厂方法模式的优缺点
优点:
①用户只需要知道具体的工厂名称就可以得到所要的产品,无需知道产品的具体创造过程
②在系统增加新产品时只需要添加具体产品类对应产品的具体工厂类,无需对原工厂进行任何修改,满足开闭原则
缺点:
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度
六、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
介绍
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
实现:
现在咖啡店业务发生改变,不仅要生产咖啡还要生产甜品,如提拉米苏、抹茶慕斯,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂,容易发生类爆炸情况。
其中拿铁咖啡和美式咖啡属于一个产品等级,都是咖啡;提拉米苏和抹茶慕斯也是一个产品登记;拿铁和提拉米苏是同一产品族(都是意大利风味),美式咖啡和抹茶慕斯是同一个产品族(属于美式风味)。这个案例就可以使用抽象工厂模式实现
先创建一个抽象类的接口里边接口方法为可以生产咖啡和甜品
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: DessertFactory
* @Description: 甜品工厂
* @Date: 2022/4/1 15:51
**/
public interface DessertFactory {
// 生产咖啡的功能
Coffee createCoffee();
// 生产甜品的功能
Dessert createDesert();
}
创建抽象咖啡类和甜品类
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: Coffee
* @Description: 咖啡类
* @Date: 2022/4/1 11:15
**/
public abstract class Coffee {
public abstract String getName();
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("加奶");
}
}
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: Dessert
* @Description: 甜品的一个抽象类
* @Date: 2022/4/1 15:41
**/
public abstract class Dessert {
public abstract void show();
}
在创建对应的具体实现类:美式咖啡、拿铁咖啡、抹茶慕斯、提拉米苏
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: AmericanCoffee
* @Description: 美式咖啡
* @Date: 2022/4/1 11:22
**/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: LatteCoffee
* @Description: 拿铁咖啡
* @Date: 2022/4/1 11:21
**/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁咖啡";
}
}
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: MatchaMousse
* @Description: 一个抹茶慕斯类
* @Date: 2022/4/1 15:48
**/
public class MatchaMousse extends Dessert{
@Override
public void show() {
System.out.println("抹茶慕斯");
}
}
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: Tiramisu
* @Description: 提拉米苏类
* @Date: 2022/4/1 15:46
**/
public class Tiramisu extends Dessert{
@Override
public void show() {
System.out.println("提拉米苏");
}
}
创建对应风味的工厂来创建对应的实例对象并实现抽象接口中的方法
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: ItalyDessertFactory
* @Description:
* 意大利风味的甜品工厂
* 生产:拿铁咖啡和提拉米苏
* @Date: 2022/4/1 15:56
**/
public class ItalyDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
@Override
public Dessert createDesert() {
return new Tiramisu();
}
}
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: AmericanDessertFactory
* @Description:
* 美式风味的甜品工厂
* 生产:美式咖啡和抹茶慕斯
* @Date: 2022/4/1 15:53
**/
public class AmericanDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
@Override
public Dessert createDesert() {
return new MatchaMousse();
}
}
客户代码
package SP_04_AbstractFactory;
/**
* @Author: {LZG}
* @ClassName: Client
* @Description: TODO
* @Date: 2022/4/1 15:59
**/
public class Client {
public static void main(String[] args) {
// 创建的是意大利风问甜品工厂对象
ItalyDessertFactory factory = new ItalyDessertFactory();
// 也可以创建美式风味的甜品加工工厂
// AmericanDessertFactory factory = new AmericanDessertFactory();
// 获取拿铁和提拉米苏
Coffee coffee = factory.createCoffee();
Dessert desert = factory.createDesert();
System.out.println(coffee.getName());
desert.show();
}
}
测试结果
抽象工厂模式的优缺点
优点:
当一个产品族种的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族种的对象
缺点:
当产品族种需要增加一个新的产品时,所有的工厂类都需要进行修改。
使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
注意事项:产品族难扩展,产品等级易扩展。