Задний план
В настоящее время у меня есть ситуация, когда у меня есть объект, который передается и принимается устройством. Это сообщение имеет несколько конструкций, а именно:
public void ReverseData()
public void ScheduleTransmission()
ScheduleTransmission
Метод необходимо вызвать ReverseData
метод всякий раз , когда он вызывается. Тем не менее, бывают случаи, когда мне нужно вызывать ReverseData
извне (и я должен добавить вне пространства имен полностью ), из которого создается объект в приложении.
Что касается «получения», я имею в виду, что ReverseData
он будет вызываться извне в object_received
обработчике событий для отмены обращения данных.
Вопрос
В целом допустимо ли для объекта вызывать свои публичные методы?
c#
design-patterns
object-oriented
methods
шпионить
источник
источник
Ответы:
Я бы сказал, что это не только приемлемо, но и поощряется, особенно если вы планируете разрешать расширения. Чтобы поддерживать расширения класса в C #, вам необходимо пометить метод как виртуальный в соответствии с комментариями ниже. Однако вы можете задокументировать это, чтобы кто-то не удивился, когда переопределение ReverseData () изменит способ работы ScheduleTransmission ().
Это действительно сводится к дизайну класса. ReverseData () звучит как фундаментальное поведение вашего класса. Если вам нужно использовать это поведение в других местах, вы, вероятно, не хотите иметь другие версии этого. Вам просто нужно быть осторожным, чтобы не допустить попадания деталей, специфичных для ScheduleTransmission (), в ReverseData (). Это создаст проблемы. Но так как вы уже используете это за пределами класса, вы, вероятно, уже продумали это.
источник
ReverseData()
является абстрактным, независимо от того, что вы делаете в дочернем классе, всегда будет вызываться оригинальный метод.virtual
... И если вы переопределяете метод, то по определению он должен бытьvirtual
илиabstract
.virtual
тоже работает. Во всех случаях, согласно OP, подпись таковаpublic void ReverseData()
, что часть ответа о переопределении вещи немного вводит в заблуждение.abstract
? Я посмотрел ответы наabstract
vsvirtual
и не увидел, что упомянуто: довольно абстрактные просто означает, что в этой базе не дано никакой версии.Абсолютно.
Видимость метода имеет единственную цель - разрешить или запретить доступ к методу вне класса или внутри дочернего класса; публичные, защищенные и приватные методы всегда могут быть вызваны внутри самого класса.
В вызове публичных методов нет ничего плохого. Иллюстрация в вашем вопросе является прекрасным примером ситуации, когда два публичных метода - это именно то, что вам следует делать.
Тем не менее, вы можете внимательно следить за следующими шаблонами:
Метод
Hello()
вызываетWorld(string, int, int)
так:Избегайте этой картины. Вместо этого используйте необязательные аргументы . Необязательные аргументы облегчают обнаружение: вызывающий пользователь
Hello(
не обязательно будет знать, что существует метод, позволяющий вызывать метод со значениями по умолчанию. Необязательные аргументы также самодокументированы.World()
не показывает вызывающему абоненту, каковы фактические значения по умолчанию.Метод
Hello(ComplexEntity)
вызываетWorld(string, int, int)
так:Вместо этого используйте перегрузки . Причина та же: лучшая открытость. Вызывающий может сразу увидеть все перегрузки через IntelliSense и выбрать правильный.
Метод просто вызывает другие общедоступные методы, не добавляя существенного значения.
Подумай дважды. Вам действительно нужен этот метод? Или вы должны удалить его и позволить вызывающей стороне вызывать различные методы? Подсказка: если название метода кажется неправильным или его сложно найти, вам, вероятно, следует удалить его.
Метод проверяет ввод и затем вызывает другой метод:
Вместо этого
World
следует проверить его параметры самостоятельно или быть закрытым.источник
Если что-то общедоступно, оно может быть вызвано в любое время любой системой. Там нет причин, вы не можете быть одной из этих систем тоже!
В высоко оптимизированных библиотеках вы хотите скопировать идиому, изложенную в
java.util.ArrayList#ensureCapacity(int)
.ensureCapacity
ensureCapacity
является публичнымensureCapacity
имеет все необходимые проверки границ и значения по умолчанию и т. д.ensureCapacity
звонкиensureExplicitCapacity
ensureCapacityInternal
ensureCapacityInternal
является частнымensureCapacityInternal
имеет минимальную проверку ошибок, потому что все входные данные поступают из классаensureCapacityInternal
ТАКЖЕ звонитensureExplicitCapacity
ensureExplicitCapacity
ensureExplicitCapacity
ТАКЖЕ частныйensureExplicitCapacity
не имеет проверки ошибокensureExplicitCapacity
делает реальную работуensureExplicitCapacity
не вызывается нигде, кроме какensureCapacity
иensureCapacityInternal
Таким образом, код, которому вы доверяете, получает привилегированный (и более быстрый!) Доступ, потому что вы знаете, что его входные данные хороши. Код, которому вы не доверяете, проходит определенную строгость, чтобы проверить его целостность и взломать или предоставить значения по умолчанию или иным образом обработать неверные данные. Оба они направляются в то место, которое действительно работает.
Однако это используется в ArrayList, одном из наиболее часто используемых классов в JDK. Вполне возможно, что ваше дело не требует такого уровня сложности и строгости. Короче говоря, если бы все
ensureCapacityInternal
звонки были замененыensureCapacity
звонками, производительность все равно была бы действительно очень хорошей. Это микрооптимизация, вероятно, только после тщательного рассмотрения.источник
Уже было дано несколько хороших ответов, и я также согласен с ними, что да, объект может вызывать свои публичные методы из других своих методов. Тем не менее, есть небольшая оговорка дизайна, которую вы должны остерегаться.
Публичные методы обычно имеют контракт: «возьмите объект в согласованном состоянии, сделайте что-нибудь разумное, оставьте объект в (возможно, другом) согласованном состоянии». Здесь «непротиворечивый» может означать, например, что
Length
aList<T>
не больше, чем егоCapacity
и что ссылка на элементы с индексами от0
toLength-1
не сгенерирует.Но внутри методов объекта объект может находиться в несовместимом состоянии, поэтому, когда вы вызываете один из ваших открытых методов, он может сделать очень неправильную вещь, потому что он не был написан с такой возможностью. Так что, если вы планируете вызывать ваши публичные методы из других ваших методов, убедитесь, что их контракт «возьмите объект в какое-то состояние, сделайте что-нибудь разумное, оставьте объект в (возможно, другом) (возможно, несовместимом - но только если исходном государство было непоследовательным) государство ".
источник
Другим примером, когда вызов публичного метода внутри другого публичного метода совершенно нормально, является подход CanExecute / Execute. Я использую его, когда мне нужно и подтверждение и сохранение инварианта .
Но в целом я всегда осторожен с этим. Если метод
a()
вызывается внутри методаb()
, это означает, что методa()
является деталью реализацииb()
. Очень часто это указывает на то, что они принадлежат к разным уровням абстракции. И тот факт, что они оба публичны, заставляет меня задуматься, является ли это нарушением принципа единой ответственности или нет.источник
Извините, но мне придется не согласиться с большинством других ответов «да, можете» и сказать, что:
Я бы не рекомендовал классу вызывать один публичный метод из другого
Есть несколько потенциальных проблем с этой практикой.
1: бесконечный цикл в унаследованном классе
Таким образом, ваш базовый класс вызывает method1 из method2, но затем вы или кто-то другой наследует его и скрывает method1 новым методом, который вызывает method2.
2: события, ведение журнала и т. Д.
Например, у меня есть метод Add1, который запускает событие «1 добавлен!» Я, вероятно, не хочу, чтобы метод Add10 вызывал это событие, записывал в журнал или что-то еще десять раз.
3: многопоточность и другие тупики
Например, InsertComplexData открывает соединение с БД, запускает транзакцию, блокирует таблицу, затем вызывает InsertSimpleData, открывает соединение, запускает транзакцию и ожидает разблокировки таблицы ....
Я уверен, что есть и другие причины, один из ответов касался «вы редактируете method1 и удивляетесь, что method2 начинает вести себя по-другому»
Обычно, если у вас есть два открытых метода, которые совместно используют код, лучше, чтобы они оба вызывали закрытый метод, а не один вызывал другой.
Редактировать ----
Давайте рассмотрим конкретный случай в ОП.
у нас не так много деталей, но мы знаем, что ReverseData вызывается каким-либо обработчиком событий, а также методом ScheduleTransmission.
Я полагаю, что обратные данные также изменяют внутреннее состояние объекта
Учитывая этот случай, я бы подумал, что безопасность нити будет важна, и, следовательно, применимо мое третье возражение против практики.
Чтобы сделать поток ReverseData безопасным, вы можете добавить блокировку. Но если ScheduleTransmission также должен быть потокобезопасным, вы захотите использовать такую же блокировку.
Самый простой способ сделать это - переместить код ReverseData в закрытый метод и вызвать его для обоих открытых методов. Затем вы можете поместить оператор блокировки в Public Methods и совместно использовать объект блокировки.
Очевидно, вы можете утверждать, что "этого никогда не случится!" или «Я мог бы запрограммировать блокировку другим способом», но суть хорошей практики кодирования заключается в том, чтобы сначала хорошо структурировать ваш код.
В академическом плане я бы сказал, что это нарушает L в твердом теле. Публичные методы больше, чем просто общедоступные. Они также могут быть изменены его наследниками. Ваш код должен быть закрыт для модификации, что означает, что вы должны подумать о том, что вы делаете, как в открытых, так и в защищенных методах.
Вот еще один: вы также можете нарушить DDD. Если ваш объект является объектом домена, его открытые методы должны быть терминами домена, которые что-то значат для бизнеса. В этом случае очень маловероятно, что «купить дюжину яиц» - это то же самое, что «купить 1 яйцо 12 раз», даже если оно начинается таким образом.
источник