Java中开闭原理的应用

Posted

技术标签:

【中文标题】Java中开闭原理的应用【英文标题】:Application of open/closed principle in Java 【发布时间】:2016-03-15 09:02:36 【问题描述】:

我尝试了解 SOLID 原理,因此实现了一些 java sn-ps。我现在关心的是OCP。有以下样本,

public abstract class Bakery

 public abstract Bakegood bake();


/******************************************/

public class BreadBakery extends Bakery 
 @Override
 public Bakegood bake() 
    return new Bread();
 


/******************************************/

public class CakeBakery extends Bakery 
 @Override
 public Bakegood bake() 
    return new Cake();
 


/******************************************/

我怎样才能创建合适的面包店。假设一位顾客来到面包店并说:“请给我两个蛋糕!”,那么我该如何实例化 CakeBakery。当然,我可以创建一个抽象工厂,例如:

public static Bakery createBakery(final String orderedBakegood)

 switch(bakegood)
 
  case "Cake": return new CakeBakery();
  case "Bread": return new BreadBakery();
  default: throw new InvalidBakeryException();
 

但我不想使用 switch 或 if 语句。是否还有其他可能,或者我的理解完全错误?

【问题讨论】:

switch 或 if 语句有什么问题?你需要以某种方式做出决定。 开关可以工作,但您需要将其打开以进行扩展。这意味着您应该能够覆盖它,因此该方法不应该是静态的。另一种解决方案是反射,它更灵活。我想看看其他人对此有什么看法,我也遇到过这个问题。 @Filkolev 考虑到他正在努力遵守良好的标准和模式,在这里使用反射就像是在打自己的脸,因为你想粉刷你的墙。 @Kayaman 我不能再反对了 :) 你们能否支持您的说法,即在这种特殊情况下反射是一种糟糕的编码实践? 【参考方案1】:

开闭原则说:

软件实体(类、模块、函数等)应该是开放的 用于扩展,但关闭以供修改。

因此,无论何时引入新的 Bakery,都不应修改现有代码。您可以为 Bakery 课程使用注册表之类的东西。如果您需要添加新的 Bakery,只需扩展 Bakery 类并注册它(例如在启动时)。因此不需要“if”或“switch”修改。此外,添加新的 Bakery 不需要更改注册表的内部结构或调用注册表的代码。

此外,此技术不依赖于您注册组件的方式。您可以使用配置文件(xml,...),通过类路径扫描,以编程方式执行此操作,...

你可以在 Spring 框架中看到很多这种方法。基本上,Spring 框架是许多设计原则的重要来源。

在这里你可以看到一个非常简单的注册表实现。

public class BakeryRegistry 

    private Map<String, Bakery> mapping = new HashMap<>();

    public BakeryRegistry() 
        loadDefaultMappingFromConfigFile();
    

    public Bakery getBakery(String name) 
        return mapping.get(name);
    

    public void registerBakery(String name, Bakery bakery) 
        mapping.put(name, bakery);
    

    private void loadDefaultMappingFromConfigFile() 
        ...
    

也许文章Killing Switch Statements With A Registry 可以提供帮助。它是基于 javascript 的,但原理是一样的。

【讨论】:

地图看起来很有趣,尤其是在添加新面包店时地图内部不需要更改。【参考方案2】:

人为的抽象导致了这里的问题。顾客不会向面包店索要抽象的“烘焙食品”,因为所有烘焙食品都不可替代。面包不能代替蛋糕,反之亦然。将这两种不同的产品包装到一个继承层次结构中违反了 Liskov 替换原则。

SOLID 原则是相互关联的,因此如果不先应用 LSP,应用 OCP 将很困难或不可能。请参阅 introduction to LSP,其中 Robert Martin 将继承描述为对 OCP 至关重要,并继续将 LSP 描述为对继承至关重要。

【讨论】:

以上是关于Java中开闭原理的应用的主要内容,如果未能解决你的问题,请参考以下文章

开闭原理及扩展功能

带构造函数的开闭原理

开闭原则原理

针对不同过滤条件的开闭原理

getStructuringElement函数以及开闭腐蚀膨胀原理讲解

Android架构组件使用和原理分析:ViewModel+LiveData