设计代码时,您总有两个选择。
- 只要完成它,在这种情况下,几乎所有解决方案都可以为您工作
- 迷恋并设计一种解决方案,该解决方案利用语言的怪癖及其意识形态(在这种情况下为OO语言-使用多态性作为提供决策的手段)
我不会集中讨论这两者中的第一个,因为实际上没有什么可说的。如果您只是想使其工作,可以将代码保持原样。
但是,如果您选择按照传统的方式进行设计并实际解决了设计模式中的问题,将会发生什么?
您可能正在看以下过程:
设计OO代码时,代码中的大多数if
s都不必在那里。自然地,如果要比较两个标量类型(例如int
s或float
s),则可能会有一个if
,但是如果要基于配置更改过程,则可以使用多态来实现所需的结果,然后移动决策(if
s)从您的业务逻辑到实例化对象的地方- 工厂。
到目前为止,您的过程可以通过4条单独的路径:
data
既未加密也未压缩(不调用,返回data
)
data
被压缩(调用compress(data)
并返回)
data
已加密(调用encrypt(data)
并返回)
data
被压缩和加密(调用encrypt(compress(data))
并返回)
仅通过查看4条路径,您就会发现问题。
您有一个进程调用不同的方法来处理数据,然后将其返回,该过程调用3(理论上为4,如果您算不上调用任何东西)。这些方法具有不同的名称,即所谓的公共API(方法通过其传达其行为的方式)不同。
使用适配器模式,我们可以解决已经出现的名称colision(我们可以统一公共API)。简而言之,适配器可帮助两个不兼容的接口一起工作。另外,适配器通过定义新的适配器接口来工作,该接口试图将其API实现统一。
这不是具体的语言。这是一种通用方法,其中的any关键字表示它可以是任何类型,在C#这样的语言中,您可以将其替换为通用(<T>
)。
我要假设,现在您可以拥有两个负责压缩和加密的类。
class Compression
{
Compress(data : any) : any { ... }
}
class Encryption
{
Encrypt(data : any) : any { ... }
}
在企业环境中,即使是这些特定的类也很可能会被接口替换,例如class
关键字将替换为interface
(应该使用C#,Java和/或PHP之类的语言)或class
关键字将保留,但是如果您使用C ++编写代码Compress
,则Encrypt
方法将被定义为纯虚函数。
为了制作适配器,我们定义了一个公共接口。
interface DataProcessing
{
Process(data : any) : any;
}
然后,我们必须提供接口的实现以使其有用。
// when neither encryption nor compression is enabled
class DoNothingAdapter : DataProcessing
{
public Process(data : any) : any
{
return data;
}
}
// when only compression is enabled
class CompressionAdapter : DataProcessing
{
private compression : Compression;
public Process(data : any) : any
{
return this.compression.Compress(data);
}
}
// when only encryption is enabled
class EncryptionAdapter : DataProcessing
{
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(data);
}
}
// when both, compression and encryption are enabled
class CompressionEncryptionAdapter : DataProcessing
{
private compression : Compression;
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(
this.compression.Compress(data)
);
}
}
这样,您最终得到4个类,每个类做的事情完全不同,但是每个类提供相同的公共API。该Process
方法。
在业务逻辑中,在处理无/加密/压缩/两者决策的情况下,将设计对象以使其依赖于DataProcessing
我们之前设计的接口。
class DataService
{
private dataProcessing : DataProcessing;
public DataService(dataProcessing : DataProcessing)
{
this.dataProcessing = dataProcessing;
}
}
然后,过程本身可以像这样简单:
public ComplicatedProcess(data : any) : any
{
data = this.dataProcessing.Process(data);
// ... perhaps work with the data
return data;
}
没有更多的条件。该类DataService
不知道将数据传递给dataProcessing
成员时将如何处理数据,它并不真正在乎它,这不是它的责任。
理想情况下,您将进行单元测试来测试您创建的4个适配器类,以确保它们起作用,并通过测试。而且,如果它们通过了,那么您可以肯定,无论您在代码中的什么位置调用它们,它们都将起作用。
因此,以这种方式进行操作后if
,我的代码中将永远不会再包含s了?
不。您在业务逻辑中不太可能有条件,但条件仍然必须存在。这个地方是你的工厂。
这很好。您将创建和实际使用代码的关注分开了。如果您使工厂可靠(在Java中甚至可以使用Google 的Guice框架之类的东西),那么在业务逻辑中,您就不必担心选择合适的注入类。因为您知道您的工厂在工作,并将按要求交付。
是否需要所有这些类,接口等?
这使我们回到了开始。
在OOP中,如果您选择使用多态性的路径,那么真的要使用设计模式,想要利用语言的功能和/或想要遵循一切都是对象意识形态,那么它就是。即便如此,此示例甚至都没有显示您将需要的所有工厂,如果要重构Compression
和Encryption
类并使其成为接口,则还必须包括其实现。
最后,您将获得数百个小类和接口,它们专注于非常具体的事情。如果您要做的只是做两个数字相加之类的简单操作,那并不一定很糟糕,但对于您来说可能不是最佳解决方案。
如果您想快速完成它,可以使用Ixrec的解决方案,该解决方案至少设法消除了else if
and else
块,我认为这甚至比普通的还要差if
。
考虑到这是我进行良好OO设计的方式。在过去的几年中,这是对接口而不是对实现进行编码的方式,这是我最喜欢的方法。
我个人更喜欢if-less编程,并且会更欣赏在5行代码上的更长解决方案。这是我习惯于设计代码的方式,并且非常乐于阅读。
更新2:关于我的解决方案的第一个版本进行了激烈的讨论。讨论主要是由我引起的,对此我深表歉意。
我决定以某种方式编辑答案,这是查看解决方案的一种方式,但不是唯一的一种方式。我还删除了装饰器部分,而我指的是立面,最后我决定完全省略,因为适配器是立面的变体。
if
陈述吗?