【设计模式】工厂方法模式详解

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目目的;所以说,工厂模式最大的优点就是解耦。

简单工厂(不属于GOF23种设计模式之一):

不是一种设计模式,更像是一种编码习惯

要学习工厂方法,必须先了解什么是简单工厂

结构
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现或者继承抽象产品的子类(即产品本身)。
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来进行产品的创建
优点

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点

增加新产品时还是需要修改工厂类的代码,违背了"开闭原则"

java 复制代码
// 定义一个接口或抽象类
interface Shape {
    void draw();
}

// 定义具体的产品类
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle...");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle...");
    }
}

// 定义简单工厂类
class ShapeFactory {
    // 工厂方法,根据输入参数决定创建哪种产品
    public static Shape getShape(String type) {
        if ("circle".equalsIgnoreCase(type)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(type)) {
            return new Rectangle();
        } else {
            throw new IllegalArgumentException("Invalid shape type: " + type);
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用简单工厂创建不同形状的对象
        Shape circle = ShapeFactory.getShape("circle");
        circle.draw();  // 输出: Drawing a circle...

        Shape rectangle = ShapeFactory.getShape("rectangle");
        rectangle.draw();  // 输出: Drawing a rectangle...

        // 尝试创建无效类型
        try {
            Shape invalidShape = ShapeFactory.getShape("triangle");
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }
    }
}

在这个例子中,ShapeFactory是简单工厂,它负责根据传入的字符串类型创建相应的形状对象。客户端代码只需调用工厂方法,不需要关心具体的创建过程。

工厂方法

针对上例中的缺点,使用工厂方法模式就可以完美的解决完全遵循开闭原则。

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

结构
  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(Concreteractory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct实现了抽象产品角色所定义的接口,由县体工厂来创建,它同县体工厂之间一一对应

将简单工厂模式升级为工厂方法模式,我们需要将创建具体产品的逻辑移到每个具体工厂类中,而不是集中在一个静态工厂方法中。以下是将简单工厂模式转换为工厂方法模式的Java代码示例:

java 复制代码
// 定义一个接口或抽象类
interface Shape {
    void draw();
}

// 定义具体的产品类
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle...");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle...");
    }
}

// 定义抽象工厂类
abstract class ShapeFactory {
    // 抽象工厂方法,由子类实现以创建具体产品
    public abstract Shape getShape();
}

// 定义具体工厂类
class CircleFactory extends ShapeFactory {
    @Override
    public Shape getShape() {
        return new Circle();
    }
}

class RectangleFactory extends ShapeFactory {
    @Override
    public Shape getShape() {
        return new Rectangle();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用工厂方法模式创建不同形状的对象
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.getShape();
        circle.draw();  // 输出: Drawing a circle...

        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.getShape();
        rectangle.draw();  // 输出: Drawing a rectangle...

        // 如果需要添加更多形状,只需要创建更多的具体工厂类即可,这里不再演示创建三角形的工厂类
    }
}

在这个工厂方法模式的示例中,我们创建了一个抽象工厂类ShapeFactory,并在其中定义了抽象方法getShape()。然后,我们分别为每种形状创建了具体工厂类(如CircleFactory和RectangleFactory),并在这些类中实现创建相应形状对象的逻辑。客户端代码现在可以根据需求实例化不同的具体工厂类来创建所需形状对象。

优点
  1. 用户只需要知道具体工厂的名称就可得到所要的产品无须知道产品的具体创建过程
  2. 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
缺点

每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

典型案例 Collection.iterator
java 复制代码
List<String> list = Arrays.asList("a","b","c","d");
Iterator<String> iterator = list.iterator();
// ArrayList部分源码
public interface AbstractList<E> extends AbstractCollection<E> implements List<E>  {
	 // Iterators
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    public ListIterator<E> listIterator() {
        return listIterator(0);
    }

    public ListIterator<E> listIterator(final int index) {
        rangeCheckForAdd(index);
        return new ListItr(index);
    }
    private class Itr implements Iterator<E>{...}
    
    private class ListItr extends Itr implements ListIterator<E>{...}

}
// Collection 部分源码
public interface Collection<E> extends Iterable<E> {
	 Iterator<E> iterator();
}
// Interator部分源码
public interface Iterator<E> {
	boolean hasNext();
    E next();
}

在Collection中定义了Iterator的抽象方法,而ArrayList又继承Collection,并在类中重写了Interator的方法。

也就是说ArrayList类是在工厂类中创建了Iterator 和ListIterator 的产品。即:

  • Collection接口是抽象工厂类,
  • ArrayList是具体的工厂类;
  • Iterator接口是抽象商品类,
  • ArrayList类中的Iter和ListItr是具体的商品类。
    在具体的工厂类中iterator()方法是创建具体的商品类对象。
相关推荐
晨米酱12 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机17 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机18 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机18 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤19 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机2 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机2 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴2 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤2 天前
工厂模式
设计模式