当一个类的功能需要通过不同的类以不同的方式从外部访问时,分区的最佳方法是什么?希望以下示例能够明确问题:)
我有一个Java类,它访问目录中的单个位置,允许外部类对它执行读/写操作。读操作返回目录上的使用统计信息(例如,可用磁盘空间,写入次数等);显然,写操作允许外部类将数据写入磁盘。这些方法始终在同一位置工作,并从外部源(传递给构造函数)接收其配置(例如,使用哪个目录,最小磁盘空间等)。
这个类看起来像这样:
public class DiskHandler {
public DiskHandler(String dir, int minSpace) {
...
}
public void writeToDisk(String contents, String filename) {
int space = getAvailableSpace();
...
}
public void getAvailableSpace() {
...
}
}
还有很多事情要发生,但这样就足够了。
需要通过两个外部类以不同方式访问此类。一个类需要访问读操作;另一个需要访问读写操作。
public class DiskWriter {
DiskHandler diskHandler;
public DiskWriter() {
diskHandler = new DiskHandler(...);
}
public void doSomething() {
diskHandler.writeToDisk(...);
}
}
public class DiskReader {
DiskHandler diskHandler;
public DiskReader() {
diskHandler = new DiskHandler(...);
}
public void doSomething() {
int space = diskHandler.getAvailableSpace(...);
}
}
此时,两个类共享同一个类,但只应读取的类可以访问write方法。
我可以把这个课分成两部分。一个类将处理读操作,另一个类将处理写:
// NEW "UTILITY" CLASSES
public class WriterUtil {
private ReaderUtil diskReader;
public WriterUtil(String dir, int minSpace) {
...
diskReader = new ReaderUtil(dir, minSpace);
}
public void writeToDisk(String contents, String filename) {
int = diskReader.getAvailableSpace();
...
}
}
public class ReaderUtil {
public ReaderUtil(String dir, int minSpace) {
...
}
public void getAvailableSpace() {
...
}
}
// MODIFIED EXTERNALLY-ACCESSING CLASSES
public class DiskWriter {
WriterUtil diskWriter;
public DiskWriter() {
diskWriter = new WriterUtil(...);
}
public void doSomething() {
diskWriter.writeToDisk(...);
}
}
public class DiskReader {
ReaderUtil diskReader;
public DiskReader() {
diskReader = new ReaderUtil(...);
}
public void doSomething() {
int space = diskReader.getAvailableSpace(...);
}
}
此解决方案可以防止类访问不应该访问的方法,但它也会破坏封装。原始的DiskHandler类是完全自包含的,只需要通过单个构造函数配置参数。通过将功能分解为读/写类,它们都与目录有关,并且都需要使用它们各自的值进行实例化。从本质上讲,我并不真正关心重复这些问题。
我可以实现一个只提供读取操作的接口,并在类只需要访问这些方法时使用它。
界面可能如下所示:
public interface Readable {
int getAvailableSpace();
}
Reader类将实例化对象:
Readable diskReader;
public DiskReader() {
diskReader = new DiskHandler(...);
}
这种解决方案似乎很脆弱,将来很容易混淆。它不保证开发人员将来会使用正确的界面。对DiskHandler实现的任何更改也可能需要更新接口以及访问类。我比以前的解决方案更喜欢它,但不是很多。
坦率地说,这些解决方案似乎都不完美,但我不确定是否应该优先考虑另一种解决方案。我真的不想打破原来的课程,但我也不知道从长远来看界面是否会给我带来很多好处。我还缺少其他解决方案吗?
答案 0 :(得分:3)
我会使用界面,再加上一点Dependency Injection - 你没有直接在你的读者或作家类中实例化new DiskHandler
,他们接受一个相应类型的对象在他们的构造者。
因此,DiskReader
会接受Readable
,而DiskWriter
会直接获得ReadWrite
(或DiskHandler
,如果您不想要为读写模式创建一个接口,虽然我建议不这样做 - 通过interface ReadWrite extends Readable
或类似的)。如果您使用适当的界面持续注入它,您将不必担心不正确的使用。
答案 1 :(得分:1)
我认为界面也是最面向对象的方法。第一种方法基本上将你的语义相关方法集合重构为一堆小实用函数:不是你想要的。
第二种解决方案允许您班级的用户准确表达他们使用它的原因。与优秀Java代码通常声明List
,Set
和NavigableMap
而不是ArrayList
,HashSet
和TreeMap
的用户相同你的类可以声明一个变量只是Readable
或Writeable
,而不是声明对任何具体子类的依赖。
显然,某人仍然需要在某个时刻调用new
,但正如tzaman所指出的,这可以通过setter和依赖注入来处理。如果在运行时需要未知数量的它们,请注入工厂。
我很好奇:为什么您认为对DiskHandler
实施的任何更改都会导致使用Reader
的类发生更改?如果Reader
可以定义为一个稳定的接口,那么接口应该清楚地说明它的语义契约(在Javadoc中)。如果用户针对该接口进行编码,则可以在他们不知情的情况下在后台更改实现。当然,如果界面本身发生变化,他们必须改变,但这与第一个解决方案有何不同?
还有一件事需要考虑:假设您有多个线程,其中大多数需要Reader
,但其中一些线程需要Writer
,所有线程都需要同一个文件。您可以让DiskHandler
同时实现Reader
和Writer
,并将单个实例注入所有线程。可以通过在需要的地方使用适当的ReadWriteLocks
和synchronizeds
来对此对象进行内部处理。如何在您的第一个解决方案中实现这一目标?