我正在尝试编写一个解决nanograms or picross puzzles的应用程序。我正在使用C#,并且想采取一种面向对象的方法,因为我在该领域非常缺乏并且想练习。我正在写这篇文章,以使其首先在Windows控制台中运行,但此后想要使用WPF添加GUI。
问题在于我无法找出一种设计,该设计遵循了我一直在发现的有关精心设计的OOP程序的所有“规则”。当我这么说的时候,我特别指的是封装,单一职责原则和抽象。
我正在使用的重要课程包括以下内容。我只提到了感兴趣的主要功能或属性:
// This represents a single row or column within a PicrossTable.
// Length is the amount of blocks and blockGroupSizes is the
// amount of shaded groups of blocks.
public PicrossSegment(int length, params int[] blockGroupSizes)
string[] blocks;
BlockGroup[] blockGroups;
public int Length { get; }
public int SolvedGroups { get; }
public bool IsSolved { get; }
// This represents a group of shaded blocks in a PicrossSegment
// I need to keep track of when all groups have been accounted for
// in the PicrossSegment, and it makes sense to put it here
public BlockGroup(int size)
public int Size { get; }
public int BlocksMarked { get; set; }
public bool IsSolved { get; }
// This is the class that would actually contain all the different
// PicrossSegments.
public PicrossTable(int rows, int cols)
public PicrossTable.AddRow(PicrossSegment ps)
public PicrossTable.AddCol(PicrossSegment ps)
private PicrossSegment[] rowData;
private PicrossSegment[] colData;
// This is the class that would solve the puzzle.
public PicrossSolver()
public PicrossSolver.SolveTable(PicrossTable pt)
// This would represent a single action taken by PicrossSolver,
// such as marking a block in any given PicrossSegment.
// layoutIndex would refer to the Row or Column, pos would refer to
// the actual position within that segment
public Step(SegmentLayout layout, int layoutIndex, int pos)
public enum SegmentLayout {Row, Col}
// This would contain a list of all the steps taken and in the
// taken order, allowing for replaying of step-by-step solutions
// of how the puzzle was actually solved.
public SolutionLogger
public SolutionLogger.AddStep(Step s);
public SolutionLogger.PrintSteps();
我在这里遇到两种情况。
(1)上面显示的代码要求我破坏多层封装。 PicrossSegment()包含私有成员block和blockGroups,需要在PicrossSolver中进行访问。这意味着PicrossSolver必须到达PicrossTable,然后拉出PicrossSegments。这对我来说似乎很糟糕,因为在最坏的情况下,它会使PicrossTable看起来像个无用的中间人,但实际上应该允许PicrossSegments交换数据(类似于通过在连接列中使用字母来解决填字游戏的问题)。还应该负担指定哪个PicrossSegments表示行或列的责任,以便SolutionLog可以这样指定。 PicrossSegments知道它们是什么毫无意义。这种方法似乎使我违反了封装,因为PicrossSegment的私有数据实际上总共由三个类使用。
(2)我尝试通过简单地让PicrossSegments知道如何解决自己来进行试验,然后再次使用PicrossTable在所有Segment之间合并数据。但是,这很容易导致神级情况。该程序的所有主要逻辑都将存在于PicrossSegment()中,这将使向代码中添加其他功能变得更加困难,因为它必须解决这一问题。此外,它似乎违反了单一责任原则,因为它将由于多种原因(自我解决,来自其他细分市场的信息,数据验证)进行更新。而且,这感觉很不对劲,因为如果我想将此程序扩展到用户可以自己玩Picross拼图的游戏中,那么PicrossSegments()实际上应该只是UI设置的数据,没有解决行为。
我尝试转向Head First Design模式,因为我显然没有在OOP中完善过,并且正在寻找某种结构(同时请记住,初学者尝试将模式应用到他们的地方是一个普遍的谬误。不属于),但我什至找不到与我相关的东西。在我看来,这种程序的面向对象编程方法似乎太过分了。我看不到需要抽象或重用的地方。也许在PicrossSolver中,我可以将解决方案的概念抽象为几个以特定方式解决的类。
抛开冗长的叙述,我回到中心问题,有没有一种显而易见的方法可以做到这一点而不破坏封装或单一职责原则?