Как я могу избежать нарушения принципа инкапсуляции или единоличной ответственности? - PullRequest
0 голосов
/ 11 мая 2019

Я пытаюсь написать приложение, которое решает нанограммы или головоломки Picross . Я использую C # и хотел бы использовать объектно-ориентированный подход, так как мне совершенно не хватает этой области и мне хотелось бы попрактиковаться. Я пишу, чтобы сначала заставить его работать в консоли Windows, но потом хотел бы добавить графический интерфейс с использованием WPF.

Проблема в том, что я не могу понять дизайн, который следует всем "правилам", которые я нашел в отношении хорошо разработанных программ ООП. Когда я говорю это, я конкретно имею в виду инкапсуляцию, принцип единой ответственности и абстракцию.

Классы важности, с которыми я работаю, включают следующее. Я упомянул только основные функции или свойства, представляющие интерес:

// 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 () содержит закрытые блоки-члены и blockGroups, которые должны быть доступны в PicrossSolver. Это означает, что PicrossSolver должен войти в PicrossTable, а затем извлечь PicrossSegment. Это кажется мне плохим, потому что в худшем случае это делает PicrossTable похожим на бесполезного посредника, но на самом деле он должен позволять PicrossSegments обмениваться данными (сродни решению ряда кроссвордов с использованием буквы в соединительном столбце). Предполагается также, что ответственность за определение того, какие PicrossSegments представляют строки или столбцы, будет нести ответственность, чтобы SolutionLog мог указывать как таковой. Для PicrossSegments не имеет смысла знать, что они из себя представляют. Этот подход, кажется, приводит меня к нарушению инкапсуляции, поскольку личные данные PicrossSegment в основном используются тремя классами.

(2) Я попытался поэкспериментировать с тем, чтобы PicrossSegments знали, как решить себя, и снова использовал PicrossTable для объединения данных между всеми сегментами. Однако это очень легко приведет к ситуации с классом Бога. Вся основная логика программы будет существовать в PicrossSegment (), что сделает добавление дополнительных функциональных возможностей к коду немного более сложным, поскольку с этим придется обходиться. Кроме того, кажется, что он нарушает принцип единственной ответственности, потому что он будет обновляться по нескольким причинам (самостоятельное решение, информация из других сегментов, проверка данных). Кроме того, это просто кажется неправильным, потому что, если я захочу расширить эту программу в игру, в которой пользователи могут играть в пазлы Picross самостоятельно, тогда PicrossSegments () действительно должен быть просто набором данных пользовательским интерфейсом без поведения решения.

Я попытался обратиться к шаблонам Head First Design Patterns, так как я явно не совершенствовался в ООП и искал какую-то структуру заранее (хотя имейте в виду, что новичкам свойственно пытаться применять шаблоны там, где они не принадлежат), но я даже не смог найти ничего, что было бы важно для меня. Мне почти кажется, что ООП подход к этой программе является излишним. Я не вижу, где мне нужно абстрагироваться или использовать повторно. Возможно, в PicrossSolver, где я могу абстрагировать концепцию решения в несколько классов, которые решают определенным образом.

Помимо многословного повествования, я возвращаюсь к центральному вопросу, есть ли очевидный способ сделать это, не нарушая принцип инкапсуляции или принцип единоличной ответственности?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...