java这种设计在多大程度上违反了封装
我正在用Java设计一个图形对象。作为设计师,我怀疑这种设计是否违反了封装,但我希望从其他人那里获得一些见解
下面是图形和顶点的两个接口
图形实现负责管理图形中的顶点,包括创建、删除、确保施加的约束等。通用参数表示Type,即存储在顶点中的值的类型,以及W8,即存储在边中的权重类型:
public interface Graph<T, W>
{
/** Creates and adds a vertex with the value given to this graph */
Vertex<T> createVertex(T value);
/** Performs some arbitrary modification to the vertex given */
void modify(Vertex<T> vertex, ....);
// some other methods...
}
顶点接口用于获取和修改顶点的属性,还用于保留引用;它的实现由图的实现决定——客户机对其内部实现一无所知。它可能是这样的:
public interface Vertex<T>
{
int getValue();
/** Performs some arbitrary modification to this vertex - same as
* as in modify method in graph */
void modify(...);
// some other methods
}
我使用顶点接口的原因是,客户端可以简单地保留对所讨论顶点的引用,并将引用传递给图形。我也可以有这样的图形界面:
public interface Graph<T, W>
{
/** Creates and adds a vertex with the value given to this graph */
void createVertex(T value);
/** Performs some arbitrary modification to the vertex given */
void modify(T vertex);
// some other methods...
}
在这个图形界面中,“修改”方法总是要搜索顶点;这将是非常不切实际的,尤其是当我们想要经常修改同一个顶点时——相反,客户机可以像第一种方法那样只保留一个引用过程
说到我的问题,我对封装非常严格,这是否违反了封装的原则
就个人而言,我认为这不会违反封装,因为客户机不知道图形对象或顶点在内部执行什么。但是,我们确实泄漏了图形以某种方式使用顶点的信息(尽管没有公开确切的内部结构),而其他库没有这样做。例如,数据结构可能是图的子集,如树和LinkedList:核心Java库中的LinkedList实现不会让应用程序注意到使用了某些节点接口;我在其他各种库中看到的树实现也不公开它们的节点
因此,在严格的面向对象术语下,这种设计是否违反了封装?我也欢迎对现有的面向对象库(也在Java范围之外)的其他参考,或讨论此主题的文章
# 1 楼答案
我认为这类似于地图界面。进入图的概念以顶点的概念为前提——你不能没有一个顶点而没有另一个顶点。因此,将两者都揭露出来是完全合法的
另一方面,您在这里公开(或可能限制)了至少一些实现细节。你的
在
Graph
中,假设Vetex可通过其内容寻址。情况可能并不总是这样。我认为这对T的限制太大了我想说,为了确保封装,图本身上唯一的询问操作应该是
Set<Vertex<T>> getVertices
。其余的应该在顶点上创建操作应返回其创建的实体:
然后可以添加
Graph.createEdge(Vertex<T> v1, Vertex<T> v2)
和removeEdge(Vertex<T> v1, Vertex<T> v2)
# 2 楼答案
我认为这并不违反封装。图形的用途通常是管理顶点和边,因此将这些概念作为类型公开和使用非常有意义。在你的问题中,你引用了
LinkedList
,但它的主要目的是成为一个List
,而不是一个公开数据结构的节点。如果愿意,它可以完全安全地公开其节点,尽管大多数客户机应该只使用它的List
API破坏封装是指以不安全的方式暴露内部,例如返回一个可变集合(例如
List<Vertex<T>>
),它是一个内部数据结构,而不是通过更受控的API(例如add(Vertex<T>)
)来保护它。如果你失去了对内脏的控制,整个世界都可以玩你的私处,你对此无能为力例如,一些
Map
接口公开了一个表示键或值的集合,并将这些实现与映射本身支持的突变联系起来。这是一种既安全又方便的暴露方式考虑不要公开像内部数据结构、布尔或位标志之类的东西(也许在API中使用枚举或接口,内部需要更有效的东西)或者具有复杂操作顺序的突变,例如“先调用
init
,然后调用setup
,然后调用x
或y
或z
,然后确保dispose
”。这类东西你想通过方便的结构和常见的设计模式嵌入到你的API中