我正在用C#编写一个项目,我对OOP最佳实践有两个问题。为了简化我的问题,我简化了代码示例。
我有一个类Map
,它将信息保存到2D平铺图中。 Map类只有一个实例:
class Map {
int height
int width
InventoryGrid iGrid
TileGrid tGrid
public Map(int height, int width) { ... }
}
Map
类包含2个与地图上的项目和图块相关的网格。这些是技术上嵌套的类,因为它们不会在代码中的任何其他位置初始化。例如,InventoryGrid
类可能如下所示:
class InventoryGrid {
Dictionary<int, Item> inventory
public function SetInventory(int index, item) { ... }
public function GetInventory(int index) { ... }
}
假设我们有一个Job
类,如果操作完成,则需要更新库存。
class Job {
public void FinishedJob()
{
// I need to update the inventory.
}
}
我们假设Job
类进行事件调用或有权访问Map
实例。
第一个问题是,在内部类上调用InventoryGrid.SetInventory()函数的最佳OO方式是什么:
Map
类中创建一个getter函数,并直接调用map.iGrid.SetInventory()。Map
类中创建一个新函数,例如map.SetInventoryAt(),然后调用iGrid.SetInventory()函数。现在在我们的示例中,地图采用x和y坐标。然而,“网格”采用单个唯一的确定性索引来存储其信息。需要一个将coords转换为索引的函数。
public int CoordsToIndex(int x, int y) {...}
所有网格(InventoryGrid,TileGrid等等)都将使用此功能
我的第二个问题是,根据最佳实践OO设计,该功能应该驻留在哪里?谁负责呢?
Grid
以避免重复? C#specific:使用接口而不是超类?Map
课程中,并认为“网格”不应该知道索引转换的坐标。这会影响第一个问题吗?GridTools
的独立(静态)类,其中包含帮助方法?答案 0 :(得分:0)
我会去a)#2和b)#1选择。
如果Map
是Singleton,请将其设为静态。
a)#2因为getter暗示库存是一个成员,一个函数为你提供了更多的封装,并且在将来扩展对象模型时能够利用继承和接口。 Getter的单一值比嵌套类更好。 编辑:我看到您在#1中通过iGrid访问获取者,对于调用者来说,如何设置广告资源并不是很明显。 IMO,如果它被命名为InventoryGrid,那么使用暴露iGrid的getter也是同样可以接受的解决方案。如果您有子嵌套类,这将是一个更好的方法。
b)#1因为逻辑上避免在2个网格类需要相同方法的情况下重复,所以基础/超类是最好的方法之一。如果具有特定于实现的CoordsToIndex
功能,您可以选择每个网格来实现接口,如果是这种情况,您也可以使用抽象基本方法。
所有网格(InventoryGrid,TileGrid等等)都将使用此功能
我认为虽然两个网格将共享完全相同的实现,但InventoryGrid和TileGrid的一个CoordsToIndex
基类是您现在想要的(在您的设计中此时)。我假设CoordsToIndex依赖于两个网格中的数据,否则你想要解耦并将CoordsToIndex放在Map类中是一个好主意。
使用帮助程序类可以让它们更适合应用程序范围:配置,文件,加密,日志记录/跟踪,扩展方法等。
这两种方案的前2个选项都很好,因此您可以走上正轨。一条建议是接口允许您使用单元测试模拟,因此您可能需要编写一些单元测试来确认您可以测试您的oop架构。
答案 1 :(得分:0)
回答你的第一个问题,Google&#34;得墨忒耳法律&#34;。对于这类事情,这将为您提供良好的OOP指导。请记住,这是一个指导方针,而不是一个硬性规则。
要回答第二个问题,请将翻译代码放在Map类上,因为这是最简单的解决方案,您可以在以后轻松进行重构。如果您发现需要一堆相关的帮助程序,请创建一个新的Helpers类并将逻辑移到那里。如果您发现其他组件需要访问Maps,那么创建一个基本地图类并在那里移动逻辑。始终选择最基本的解决方案,因为当更好的解决方案变得更加清晰时,它是最容易改变的解决方案(Google YAGNI)。
话虽如此,考虑使用多维数组或锯齿状数组而不是字典。对我来说,他们更容易理解瓷砖地图,翻译问题就消失了。让问题消失比优雅解决问题更好。