问题:在做项目的时候,是不是所有包含非静态方法的类,都要写一个接口?是因为这样的目的是为了解耦,然后通过DI注入实现吗?
这不是提取接口的根本原因。因为要做到解耦和DI,直接用类也是可以的,尤其Java的普通方法都是允许子类override的。此外,如果你的接口永远都只有一个实现类,并没有任何可能的需求变化,那么还有必要解耦吗?
所以说,不能死板的将类的方法提取接口,然后美其名曰为 面向接口设计 。我们不能误解“面向接口设计”原则,该原则所指的“接口”并非Java语言中的interface类型,而是指面向调用者对外暴露的接口,代表一种交互与协作,是对信息的隐藏和封装,而不是具体的interface类型。即使是普通的java方法仍然满足隐藏细节的原则,如果是public的,就可以认为该方法是“面向接口设计”中的接口,也就是说:不要针对实现细节编程,而是针对接口编程。
针对java中的interface类型,包含了两种含义:
Runnable
, Cloneable
, Seriazable
。
例如邮件的收发业务。如果收邮件和发邮件可能会被用到不同的使用场景,换言之,这两个方法不会要求必须同时出现,那么就可以分别为其定义两个接口 EmailSender
, EmailReceiver
:
public interface EmailSender { void send(Message message); } public interface EmailReciever { Message[] receive(); }
但是,我们可以定义一个类 EmailService
同时实现这两个接口,这就是所谓的“大对象、小接口”,又或者说是 接口隔离原则
的体现。
如果你先定义了一个类叫 EmailService
,然后因为你需要定义接口对其抽象,然后就简单地将这个类的所有公有方法都提取到抽象的接口中,这样设计的接口,被Martin Fowler称为Header Interface。欲知Role Interface与Header Interface,可以参考Martin Fowler的 这篇文章
。
问题:收发邮件被用到不同的使用场景,怎么理解?
比如说,在通知模块中,就只会用到发邮件这个方法,不会用到收邮件;在垃圾邮件识别器功能中,就只会用到收邮件这个功能。
问题:那这样做有什么好处?
好处包括:
例如说转账服务,一个是转出(credit),一个是转入(debit)。一种方法是定义一个接口 Account
,提供转入和转出的方法。定义的转账服务方法为:
public interface Account { void credit(Money amount); void debit(Money amount); } void transfer(Account source, Account destination);
这个方法只是从形参的名称体现了转出源与转入目标,这种依靠参数名称对转入和转出账户的顺序做约束是不可靠的。当我们在实现 transfer()
方法时,也有可能错误地调用 Account
的方法,导致潜在的bug。
如果我们基于转出上下文和转入上下文分别识别参与的角色,就可以提出两个接口 Source
与 Destination
:
public interface Source { void credit(Money amount); } public interface Destination { void debit(Money amount); }
转账的服务方法就可以定义为:
void transfer(Source source, Destination destination);
你觉得哪个方法更安全、更可读?显然是后者,这就是建立角色接口的好处。