封装是非常有必要的。有时候,暴露太多的细节会让调用者感到无可适从。
对于TelnetService类,我们需要依序调用connect()、login()、enterUShell(),然后在执行命令后,必须依序执行exitUShell(),disconnect()。这让我想起事务处理,FTP访问等与资源有关的逻辑,都需要在执行逻辑前后包裹一些基础设施的处理逻辑。为了避免在执行命令前后忘记连接或断开telnet,最好能将此过程封装。
这是从调用安全性来考虑。
如果从调用的简洁性考虑,封装亦有必要。当我们需要通过TelnetService发送telnet命令时,为何还需要了解内部的执行逻辑呢?
那么,该如何封装才能两全其美,既满足对执行逻辑顺序的重用,又满足对命令逻辑的扩展?
通常做法是将真正的执行逻辑提取为接口,如Java中Runnable的方式。这其实可以看作Command模式的运用。当然,我更愿意看做是对函数的封装,例如Guva中的tranform()、filter()之类的方法,接受更具有函数气质的Function或者Predicate接口(当时,Java 8还未问世呢)。
因此,我的做法如下:
public class TelnetService {
public T withCommand(ExecutionCommand<T> command) {
connect();
login();
enterUShell();
T result = command.send();
exitUShell();
disconnect();
return result;
}
}
可以这样调用:
String result = telnetService.withCommand(new ExecutionCommand<String>() {
@Override
public String send() {
return telnetService.transfer();
}
}
);