#c# #.net #class #oop
#c# #.net #класс #ооп
Вопрос:
У меня есть вопрос ООП о том, как потенциально разделить большой класс на классы меньшего размера.
Приведенный ниже класс имеет много методов. Я организовал эти методы в группы. Каждая группа общедоступных методов применима только к определенному подразделу бизнес-объекта реального мира, который представляет класс. В идеале я бы предпочел разделить этот класс, например, на классы меньшего размера (это грубые названия, но они приведены в иллюстративных целях):
- MyBusinessObjectGroupOne
- MyBusinessObjectGroupTwo
- MyBusinessObjectGroupThree
Обратите внимание, что общедоступные методы возвращают один и тот же объект класса, потому что эти методы вызываются с использованием цепочки методов. Кроме того, каждая группа может и будет иметь гораздо больше методов, чем просто четыре.
public class MyBusinessObject{
private object memberOne;
private object memberTwo;
private object memberThree;
//METHOD GROUP ONE
public MyBusinessObject Method1(){ }
public MyBusinessObject Method2(){ }
public MyBusinessObject Method3(){ }
public MyBusinessObject Method4(){ }
//METHOD GROUP TWO
public MyBusinessObject Method5(){ }
public MyBusinessObject Method6(){ }
public MyBusinessObject Method7(){ }
public MyBusinessObject Method8(){ }
//METHOD GROUP THREE
public MyBusinessObject Method9(){ }
public MyBusinessObject Method10(){ }
public MyBusinessObject Method11(){ }
public MyBusinessObject Method12(){ }
//METHOD GROUP FOUR
private void Method13(){ }
private void Method14(){ }
private void Method15(){ }
private void Method16(){ }
}
Есть три аспекта этого класса, которые вызывают у меня трудности с выяснением, можно ли разделить этот класс:
- Вышеупомянутая цепочка методов
- Хотя методы в каждой общедоступной группе не вызывают методы в других общедоступных группах, методы в закрытой группе могут быть вызваны любым общедоступным методом в любой группе.
- Аналогично, доступ к членам класса и их изменение осуществляются методами во всех четырех группах.
Поэтому я не уверен, возможно ли разделить этот класс на отдельные классы по некоторым или всем этим причинам.
Я рассмотрел возможность переноса методов из частной группы в новый класс (например, MyUtils
); а также перемещения членов класса в другой новый класс (например, MyData
), экземпляр которого можно было бы передать MyUtils
через ref
.
Таким образом, я мог бы переместить каждую из групп методов в их собственный класс, передать в MyData
by ref
, и каждый из них тоже мог бы использовать MyUtils
.
Является ли это правильным подходом для пунктов 2 и 3 выше? Я не уверен в последствиях для цепочки методов переноса этих методов в новые классы.
РЕДАКТИРОВАТЬ Это ответ на соответствующий комментарий ниже. Я изучаю это, чтобы просто уменьшить размер класса. Я не стремлюсь добавить какую-либо дополнительную архитектурную гибкость или функциональность путем разделения класса. Это в основном для структуры и организации кода. Я также знаю, что код в большом классе с методами в разных отдельных логических группах может нарушать принцип единой ответственности.
Комментарии:
1. Похоже, у вас мог бы быть один абстрактный базовый класс с тремя реализациями (по одной для каждой «группы»). Тогда база могла бы содержать все общие элементы, а также четыре абстрактных метода, которые будут реализовывать производные классы
2. Почему вы хотите это сделать? Делается ли это для уменьшения размера (возможно, массивного) исходного файла, который реализует
MyBusinessObject
? Если это так, используйтеpartial
модификатор для набора отдельных файлов, все из которых объединяются для реализации вашего одного большого класса. Разделение функций означает выяснение того, как управлять состоянием, которое поддерживает класс, и предоставление способа вызова метода в одной из других реализаций класса. Абстрактные базовые классы, интерфейсы и т.д. — это просто сложности, которые будут мучить вас, пока вы будете поддерживать это.3. @JohnSteed похоже, вы выяснили, что все методы связаны с единственной причиной изменения класса. Вероятно, это не так часто, но я думаю, что это возможно. В этом случае решение с частичным классом, вероятно, является лучшим, если вам не нравится один файл с кучей кода. В конце концов, это один класс. Я бы, вероятно, пересмотрел тот факт, что они могут быть разными классами.
4. Используйте возможности компилятора для частичного создания классов. Класс может состоять из множества различных исходных файлов, если вы объявляете класс в каждом файле с помощью
partial
ключевого слова. Компилятор просто собирает фрагменты класса в каждом файле и склеивает их в единый класс. Вы можете назвать каждый файл таким образом, чтобы было ясно назначение каждого фрагмента класса. Вы получаете преимущество от чистого разделения вашего класса на куски, сохраняя при этом единый класс5. @CoolBots, да, есть. Внутренне,
Method1
никогда бы не вызвалMethod8
, но это вполне допустимый сценарий для использования вызывающимmyObj.Method1().Method8();
Ответ №1:
Вы можете создать MyBusinessObject
базовый класс, удалить оттуда методы, специфичные для групп, и оставить там только те методы, которые являются общими для всех групп с protected
модификатором access. Затем создайте три класса, производных от MyBusinessObject
, чтобы все три имели доступ к общим методам. Затем в каждый из этих классов включить методы, специфичные для группы. Это должно дать вам классы меньшего размера с возможностью создания цепочки. Что-то вроде этого:
public class MyBusinessObject {
// You might need to move these to one of the derived classes .......
private object memberOne;
private object memberTwo;
private object memberThree;
//METHOD GROUP FOUR
protected void Method13(){ }
protected void Method14(){ }
protected void Method15(){ }
protected void Method16(){ }
}
public class Group1 : MyBusinessObject
{
//METHOD GROUP ONE
public Group1 Method1(){ }
public Group1 Method2(){ }
public Group1 Method3(){ }
public Group1 Method4(){ }
}
public class Group2 : MyBusinessObject
{
//METHOD GROUP TWO
public Group2 Method5(){ }
public Group2 Method6(){ }
public Group2 Method7(){ }
public Group2 Method8(){ }
}
public class Group3 : MyBusinessObject
{
//METHOD GROUP THREE
public Group3 Method9(){ }
public Group3 Method10(){ }
public Group3 Method11(){ }
public Group3 Method12(){ }
}
Редактировать:
Исходя из пояснения, этот подход не будет работать, поскольку он может потребоваться для цепочки вызовов в разных группах.
Основываясь на этой информации, самое простое, что можно сделать IMHO, чтобы сделать код более читаемым, — это разделить его в разных файлах с помощью partial
классов. Вероятно, правильным решением было бы пересмотреть дизайн и создать меньшие, более сжатые и узкие классы, но это потребовало бы усилий по рефакторингу, которые могут быть дорогостоящими.
Комментарии:
1. Итак … почему в случае OP это лучше, чем использование
partial
ключевого слова, которое практически предназначено для этой цели?2. На самом деле, этот метод вообще не работает для случая OP — вы не можете связать вызовы между группами. Как бы вы вызвали
Group1.Method3()
объект, возвращаемыйGroup2.Method5()
?3. Это злоупотребление наследованием во всех смыслах этого слова.
4. Спасибо, CoolBots. Я понял вопрос по-другому, вероятно, он немного широковат, чтобы дать хороший ответ. Вероятно, по той же причине неточно сказать, что это злоупотребление наследованием (в ответ на комментарий @Flater), не зная реального назначения этого класса и методов.
5. @IvanVargas не беспокойтесь! Согласно ответу OP: да, есть. Внутренне,
Method1
никогда не вызывал быMethod8
, но это совершенно допустимый сценарий для использования вызывающимmyObj.Method1().Method8();
— эта проблема теперь прояснена. К сожалению, это делает ваше решение в том виде, в каком оно написано на данный момент, непрактичным.
Ответ №2:
Попробуйте сделать это с помощью расширений (псевдокод)
static class ExtensionGroup1
{
public static MyBusinessObject Method1_1(){ this MyBusinessObject onj, ... }
public static MyBusinessObject Method1_2(){ this MyBusinessObject onj, ... }
public static MyBusinessObject Method1_3(){ this MyBusinessObject onj, ... }
public static MyBusinessObject Method1_4(){ this MyBusinessObject onj, ... }
}
static class ExtensionGroup2
{
public static MyBusinessObject Method2_1(){ this MyBusinessObject onj, ... }
. . . . ..
}
Затем в вашем основном классе вы можете сделать…
class Context
{
int Var1 {get;set}
int Var2 {get;set}
}
private int _var1;
private bool _var2;
void SomeMethod(MyBusinessObject someObject)
{
var ctx = new Context(){ Var1 = _var1, Var2 = _var2 }
this.Method1_1(ctx).Method2_1(ctx)...
}
Обратите внимание на это — вы сможете смешивать методы для одной и той же или разных групп. И, особенно обратите внимание, что вашим расширениям необходимо будет использовать ваш текущий основной класс в качестве класса контекста, следовательно Method1_1(this)
Комментарии:
1. Одна из проблем с этим подходом заключается в том, что есть закрытые члены
MyBusinessObject
, к которым необходимо получить доступ вMethodX
, эти закрытые члены должны быть доступны за пределамиMyBusinessObject
2. @T.S.Свойства — это синтаксический сахар, они компилируются в методы, т. Е. для свойства
int A { get; set;}
компилятор генерирует методы get_A и set_A. Если свойство объявленоpublic int A { get; private set; }
, можно увидеть свойство за пределами его класса, но нельзя его установить. В любом случае, в вопросе есть частные поля и методы, и в нем упоминается, что к этим частным методам необходимо получить доступ в общедоступных методах, что невозможно при использовании предложенного вами подхода, если эти частные методы не будут изменены наinternal
илиpublic
.3. @IvanVargas Смотрите мой предыдущий комментарий. Тем не менее. Я все еще рассматриваю этот подход как возможное решение. Как вы видите в цепочке вызовов, я победил
.Method2_1(this)
. В любом случае, вы говорите об усилиях по рефакторингу. Итак, где вы видитеthis
, я упомянул «контекстный класс». Итак. Внутри вашегоMyBusinessObject
вы должны создать классContext
, который будет носителем данных вашего личного поля посредством выполнения метода, например,this.Method2_1(context).Method4_1(context)
. Это то, что я действительно имею в виду.