新的类实例 vs 单例类 vs 静态方法

5

我在为这种情况选择最佳设计模式方面感到困难:

当我在我的Swing用户界面上点击浏览按钮时,必须在Web浏览器中打开指定的URL。该功能是在此实用程序类中实现的,该类看起来像这样:

//inside action Listener of the browse button I call the openURL method of the below class

class webBrowserUtility(){
    void openURL(String url){
    ........
    }
}

设计模式方法

方法一)我可以创建上述类的新实例并调用openURL()。

方法二)单例:将WebbrowserUtility类设置为单例,并在内存中保留该类的静态实例,以便在需要时调用该方法。

方法三)静态方法:将openURL()方法设置为静态方法,并根据需要调用WebbrowserUtility.openURL(url)。

对于第一种方法,我担心每次单击浏览按钮都会创建WebBrowserUtility类的新实例可能会效率低下。对于选择方法2和方法3之间应该采用哪种方法我感到困惑。请您帮助我选择最适合这种情况的最佳设计模式?或是否有更好的设计模式可适用于此?


嗯,Desktop.browse()选择了第二种方法。 - Joachim Sauer
6个回答

6
对于你的问题,没有一个绝对正确的答案。也没有必要寻找唯一的答案。实际上,在设计某个软件的架构时,我们应该了解:当你不知道什么是正确的时候,使用抽象方法。然后,如果你最终发现你的初始实现并不正确,你只需用另一个实现替换它,而无需改变公共接口。
我的意思是,在你的情况下,这并不明显:从一方面来看,你应该使用单例模式,这样就不需要实例化多个实例,消耗更多内存并在对象创建时引入额外的开销。然而,从另一方面来看,你不能确定项目要求不会改变——也许你将不得不为你的WebBrowserUtility服务添加一些状态,并保持不同消费者的不同状态并行调用,甚至可能想要实现实例池化。你无法预测未来,所以应该使用抽象。
抽象对象实例化的最简单方法是使用一个“静态工厂方法”,并像getInstance()那样调用它——就像你要实现一个单例模式一样,但不要仅仅考虑单例模式的实现——这只是实例化的一个抽象。目前,你可以坚持使用单例模式,因此这个方法将始终返回服务的唯一实例,但在未来,如果你需要改变类的创建方式,只需更改getInstance()的实现,而无需更改任何实际使用你的服务的代码。因此,在你有足够的信息选择正确方式之前,你不必立即做出决定。
因此,使用工厂方法实例化你的服务能够给你更大的灵活性,但是还有更好的方法。如果你继续开发这个关于实例化抽象的想法,你会发现通常你会将创建代码移出服务本身到一个专门的工厂类中,因为有时你希望用不同的方式实例化相同的服务。进一步发展这个想法最终将使用控制反转模式,这是如何创建你的服务实例和管理它们之间依赖关系的终极答案。
如果你使用Java,请看看非常流行的Spring框架,它允许你在配置文件中定义服务创建规则,所以如果你需要原型而不是单例,只需要改变那个配置文件。
我不建议将您的服务类实现为一些具有必要逻辑的静态方法。原因在于,虽然现在实现起来可能简单快捷,但是在未来,您会受到定义静态方法接口的限制。因此,如果您最终需要使用多个实例,您还需要更改使用您的静态方法的消费者代码,因为静态签名在接口中而不仅仅在您的实现中。相反,在原型/单例之间进行决策隐藏在实例化代码中,因此消费者不关心实例是如何创建的——他们只是请求一个实例并获得它。

2

在这种情况下,我个人会选择使用静态方法,因为您的openURL方法不使用任何类属性,因此不需要类实例来支持它。


2
首先,我建议您不要过度担心性能/效率问题,除非您有充分的理由相信这会成为一个问题。等待并查看它是否真的是一个问题,然后再相应地解决它。但你可能会发现没有什么可担心的。
那么,问题是,你的WebBrowserUtility使用了类的非静态成员变量(即实例数据)吗?
如果使用了,那么你必须选择方法1,并每次创建一个新实例。
如果没有使用,那么我倾向于选择静态方法,并将WebBrowserUtility设置为静态类,而不是实现单例模式。你不需要只有一个实例,这就是单例模式的用途。静态类上的静态方法解决方案为您提供所需的易访问性。缺点是,如果您为此编写单元测试,则正确地对静态方法进行单元测试是有问题的。

2

在我看来,我会选择第三种方法。在此示例的范围内,似乎没有必要存储对象状态,并且单击处理程序可以直接使用。

当然,这里的限制是您无法像使用单例模式一样传递类的实例。但是,即使这是必需的,也不太可能由于其缺乏状态而有一个限制实例数量的要求,因此您可能根本不应该使用单例模式。

希望这可以帮到您。


我给你点赞,因为“你和我基本上是在说同一件事”。(https://dev59.com/_lrUa4cB1Zd3GeqPnMUH#7242406) - razlebe

1

单例模式被过度使用来限制实例的数量,因为这并不像人们想象的那样经常需要。我认为它们更适用于使测试比静态方法更容易。使用静态方法意味着您无法轻松地将真实的 Web 浏览器实用程序类替换为模拟或测试双倍体。这是否是一个问题,由您决定。

在这里,我可能仍然会选择静态方法,如果需要,修改 WebBrowserUtility 类以在内部使用单例实例,并允许客户端代码将此实例设置为测试版本。请参见《与遗留代码有效地工作》中的“引入静态 setter”。


0
你需要在WebBrowserUtility类中保留状态(例如成员变量、计数器等)吗?
如果是:你需要这个状态在全局范围内可用或同步吗?采用方法2...否则采用方法1)
如果不需要:那么这就是一个简单的静态实用程序类,在静态类上使用静态方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接