打破一个类,或强制访问限制访问?

时间:2010-05-24 22:51:19

标签: java interface encapsulation

当一个类的功能需要通过不同的类以不同的方式从外部访问时,分区的最佳方法是什么?希望以下示例能够明确问题:)

我有一个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方法。

解决方案1 ​​

我可以把这个课分成两部分。一个类将处理读操作,另一个类将处理写:

// 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类是完全自包含的,只需要通过单个构造函数配置参数。通过将功能分解为读/写类,它们都与目录有关,并且都需要使用它们各自的值进行实例化。从本质上讲,我并不真正关心重复这些问题。

解决方案2

我可以实现一个只提供读取操作的接口,并在类只需要访问这些方法时使用它。

界面可能如下所示:

public interface Readable {
    int getAvailableSpace();
}

Reader类将实例化对象:

Readable diskReader;
public DiskReader() {
    diskReader = new DiskHandler(...);
}

这种解决方案似乎很脆弱,将来很容易混淆。它不保证开发人员将来会使用正确的界面。对DiskHandler实现的任何更改也可能需要更新接口以及访问类。我比以前的解决方案更喜欢它,但不是很多。

坦率地说,这些解决方案似乎都不完美,但我不确定是否应该优先考虑另一种解决方案。我真的不想打破原来的课程,但我也不知道从长远来看界面是否会给我带来很多好处。

我还缺少其他解决方案吗?

2 个答案:

答案 0 :(得分:3)

我会使用界面,再加上一点Dependency Injection - 你没有直接在你的读者或作家类中实例化new DiskHandler,他们接受一个相应类型的对象在他们的构造者。

因此,DiskReader会接受Readable,而DiskWriter会直接获得ReadWrite(或DiskHandler,如果您不想要为读写模式创建一个接口,虽然我建议不这样做 - 通过interface ReadWrite extends Readable或类似的)。如果您使用适当的界面持续注入它,您将不必担心不正确的使用。

答案 1 :(得分:1)

我认为界面也是最面向对象的方法。第一种方法基本上将你的语义相关方法集合重构为一堆小实用函数:不是你想要的。

第二种解决方案允许您班级的用户准确表达他们使用它的原因。与优秀Java代码通常声明ListSetNavigableMap而不是ArrayListHashSetTreeMap的用户相同你的类可以声明一个变量只是ReadableWriteable,而不是声明对任何具体子类的依赖。

显然,某人仍然需要在某个时刻调用new,但正如tzaman所指出的,这可以通过setter和依赖注入来处理。如果在运行时需要未知数量的它们,请注入工厂。

我很好奇:为什么您认为对DiskHandler实施的任何更改都会导致使用Reader的类发生更改?如果Reader可以定义为一个稳定的接口,那么接口应该清楚地说明它的语义契约(在Javadoc中)。如果用户针对该接口进行编码,则可以在他们不知情的情况下在后台更改实现。当然,如果界面本身发生变化,他们必须改变,但这与第一个解决方案有何不同?

还有一件事需要考虑:假设您有多个线程,其中大多数需要Reader,但其中一些线程需要Writer,所有线程都需要同一个文件。您可以让DiskHandler同时实现ReaderWriter,并将单个实例注入所有线程。可以通过在需要的地方使用适当的ReadWriteLockssynchronizeds来对此对象进行内部处理。如何在您的第一个解决方案中实现这一目标?