【问题标题】:Is this Simple Factory violating the Open Closed Principle?这个简单工厂是否违反了开放封闭原则?
【发布时间】:2018-06-11 03:27:30
【问题描述】:

这个简单工厂是否违反了开放封闭原则?

每次需要创建新的具体产品时,SimpleProductFactory 都需要更改,但它坚持单一职责原则,因为这是它会更改的唯一原因。它的唯一目的是让客户不违反开放封闭原则,所以我想它本身不会违反,因为显然在某个地方需要这段代码。

我对更改工厂不感兴趣,但这个具体示例是否违规。

产品

interface Product{
  public int getPrice();
}

牛奶

class Milk implements Product{
  public int getPrice(){ return 5; }
}

筹码

class Chips implements Product{
  public int getPrice(){ return 3; }
}

SimpleProductFactory

class SimpleProductFactory{

  public Product createProduct(String productName){

    if(productName.equals("milk")){
      return new Milk();
    }
    else if(productName.equals("chips")){
      return new Chips();
    }
    return null;
  }
}

客户

class Client{
  public static void main(String[] args) {
    SimpleProductFactory productFactory = new SimpleProductFactory();
    Product prod = productFactory.createProduct("milk");
    System.out.println(prod.getPrice());

  }
}

【问题讨论】:

  • 你指的是SimpleProductFactory 类吗?
  • @Ravi 我不确定你的意思。如果这是一个真正的程序,我想我会在多个地方使用 SimpleProductFactory,但 SimpleProductFactory 不会添加更多方法。
  • 我认为您想为此使用abstract factory。编辑:正如 Timothy Truckle 在他的回答中提到的 (1+)
  • 我认为 Ravi 的意思是,如果你将 SimpleProductFactory 注入到 Client 中而不是直接实例化它,你可以将工厂子类化以在不违反 OCP 的情况下改变其行为。

标签: java oop design-patterns solid-principles design-principles


【解决方案1】:

上面给出的静态工厂示例仍然可以扩展为像 Icecream 这样的其他产品,如下所示。即使我们将工厂方法设为静态,它仍然可以扩展(尽管不能通过常规方式覆盖该方法)

    class SimpleProductFactoryExt extends SimpleProductFactory {
        public Product createProduct(String productName) {
            if (productName.equals("Icecream")) {
                return new Milk();
            } else {
                return super.createProduct(productName);
            }
        }
    }

【讨论】:

    【解决方案2】:

    除了Timothy Truckle关于服务定位器的回答...

    在 Java 8 中,您可能希望使用 method referencesSupplier 接口为像您这样的简单用例实现通用工厂。

    例如

    class SimpleProductFactory {
    
        private Map<String, Supplier<? extends Product>> supplierRegistry = new HashMap<>();
    
        public void addProductSupplier(String productName, Supplier<? extends Product> productSupplier) {
            supplierRegistry.put(productName, productSupplier);
        }
    
        public Product createProduct(String productName) {
            Product product = null;
    
            Supplier<? extends Product> productSupplier = supplierRegistry.get(productName);
    
            if (productSupplier != null) {
                product = productSupplier.get();
            }
    
            return product;
        }
    } 
    

    您的客户端代码将如下所示

    class Client{
      public static void main(String[] args) {
        SimpleProductFactory productFactory = new SimpleProductFactory();
    
        productFactory.addProductSupplier("milk", Milk::new); // Constructor reference
        productFactory.addProductSupplier("chips", Chips::new);
    
        Product prod = productFactory.createProduct("milk");
        System.out.println(prod.getPrice());
    
      }
    }
    

    如你所见,简单工厂是

    • 开放扩展,因为您可以简单地添加其他产品供应商
    • 修改关闭,因为在实施其他产品时无需更改,只需添加即可。

    PS:通过更多的重构,您可以简单地将其变成任何类型的真正通用工厂。

    【讨论】:

      【解决方案3】:

      这个简单工厂是否违反了开放封闭原则?

      回答您的问题。 “是的,简单工厂违反了开放封闭原则出于某种原因。”

      应该修改简单工厂模式以帮助我们为调用者选择特定的类。如果我们让这个类符合开放封闭原则,那么我们必须将负担转移到其他类,这个类将不再用于工厂的目的。并非所有原则都是绝对的。我们需要权衡使用时或不使用时的好处。

      【讨论】:

      • 这太精确了! +1。工厂模式故意是 OCP 的一个例外。
      【解决方案4】:

      开放/封闭原则并不真正适用于工厂,因为毕竟它们是不同类型对象的来源......

      另一方面,您可以有一个 抽象工厂 使用 javas ServiceLoader 查找“真实”工厂。然后,您甚至可以在自己的 jar 中添加更多真正的工厂,而无需更改现有代码...

      【讨论】:

      • OCP 完美适用于两种 GoF 工厂模式(抽象工厂和工厂方法)。它不适用于简单工厂模式,因为它是由 Head First Design Patterns 作为教具发明的。它不适用于生产代码。
      • @jaco0646,它如何应用于工厂方法?如果你有新的实现,它们必须放在某个地方,因此必须触及这个地方。
      • @FelipeMartinsMelo ServiceLoader 在类路径中查找给定接口的实现。使用工厂的代码只需要知道工厂实现的接口(abstact factory)和生产对象实现的接口(对于所有具体工厂,两者都是相同的)。您可以通过在 new jar 的 META-INF 文件夹中创建一个文件来提供一个新工厂,该文件具有工厂实现(以及 ServiceLoader 查找)的接口的 FQN 作为包含新的 FQN 的名称工厂实现为纯文本。
      • @jaco0646,是的,从这个角度我同意。
      猜你喜欢
      • 2021-12-15
      • 2011-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-24
      • 2010-10-08
      • 1970-01-01
      相关资源
      最近更新 更多