Функция Параметр лучшие практики - PullRequest
3 голосов
/ 16 июля 2009

У меня вопрос по поводу использования параметров функции.

В прошлом я всегда писал свой код так, чтобы вся информация, необходимая для функции, передавалась как параметр. То есть глобальные параметры не используются.

Однако, просматривая код других людей, функции без параметров кажутся нормой. Я должен отметить, что они предназначены для частных функций класса и что значения, которые были бы переданы в качестве параметров, на самом деле являются частными переменными-членами этого класса.

Это приводит к аккуратному виду кода, и я начинаю склоняться к этому для частных функций, но хотел бы, чтобы мнения других людей.

например.

Start();  
Process();  
Stop();  

аккуратнее и удобочитаемее, чем:

ParamD = Start(paramA, ParamB, ParamC);  
Process(ParamA, ParamD);  
Stop(ParamC);  

Он нарушает инкапсуляцию с точки зрения метода, но не с точки зрения класса.

Ответы [ 8 ]

6 голосов
/ 16 июля 2009

В принципе нет ничего плохого в том, что функции имеют доступ к полям объекта, но конкретный пример, который вы приводите, пугает меня, потому что цена упрощения ваших вызовов функций заключается в том, что вы запутываете жизненный цикл своих данных.

Чтобы перевести пример args в поля, вам нужно что-то вроде:

void Start() {
    // read FieldA, FieldB, and FieldC
    // set the value of FieldD
}

void Process() {
    // read FieldA and do something
    // read FieldD and do something
}

void Stop() {
    // read the value of FieldC
}

Start() устанавливает FieldD побочным эффектом. Это означает, что, вероятно, нельзя звонить Process() до тех пор, пока вы не позвоните Start(). Но код не говорит вам этого. Вы только узнаете, выполнив поиск, чтобы увидеть, где FieldD инициализируется. Это просит ошибок.

Мое эмпирическое правило заключается в том, что функции должны обращаться к полю объекта только в том случае, если всегда безопасно для доступа к этому полю. Лучше всего, если это поле инициализируется во время создания, но поле, которое хранит ссылку на объект соавтора или что-то, что может со временем измениться, тоже хорошо.

Но если не допустимо вызывать одну функцию, за исключением случаев, когда другая функция выдала какой-либо вывод, этот вывод должен быть передан, а не сохранен в состоянии. Если вы рассматриваете каждую функцию как независимую и избегаете побочных эффектов, ваш код будет более понятным и понятным.

4 голосов
/ 16 июля 2009

Как вы упомянули, между ними есть компромисс. Нет строгого правила всегда отдавать предпочтение одному. Минимизация области видимости переменных сохранит их побочный эффект локальным, код станет более модульным и многократно используемым, а отладка станет проще. Однако в некоторых случаях это может быть излишним. Если вы сохраняете свои классы маленькими (что вам следует делать), тогда переменная общего доступа будет иметь смысл. Однако могут быть и другие проблемы, такие как безопасность потоков, которые могут повлиять на ваш выбор.

2 голосов
/ 16 июля 2009

Это то, что вам нужно измерить в каждом конкретном случае.

Например, спросите себя, должны ли вы использовать параметр в приватном методе. Будет ли разумным передавать значение, отличное от значения конкретного свойства объекта? Если нет, то вы также можете получить доступ к свойству / полю непосредственно в методе.

OTH Вы можете спросить себя, мутирует ли этот метод состояние объекта? Если нет, то, возможно, он будет лучше как статический, и все его обязательные значения будут переданы в качестве параметров.

Существуют всевозможные соображения, самое верхнее должно быть "Что наиболее понятно для других разработчиков".

2 голосов
/ 16 июля 2009

Обычная практика - не передавать собственные атрибуты-члены объекта в качестве параметров его методам: фактически, когда вы вызываете myobject.someMethod (), вы неявно передаете весь объект (со всеми его атрибутами) как параметр в код метода.

1 голос
/ 16 июля 2009

В объектно-ориентированном языке обычно передаются зависимости (классы, с которыми этот класс будет взаимодействовать) и значения конфигурации в конструкторе, и только те значения, которые фактически должны использоваться при вызове функции.

Это на самом деле может быть более читабельным. Рассмотрим код, где у вас есть служба, которая генерирует и публикует счет. Существует несколько способов сделать публикацию - через веб-сервис, который отправляет ее на какой-то централизованный сервер, или по электронной почте, отправленной кому-то на складе, или, возможно, просто отправив ее на принтер по умолчанию. Однако обычно методу, вызывающему Publish (), проще не знать специфики того, как происходит публикация, - ему просто нужно знать, что публикация прошла без проблем. Это позволяет вам думать о меньшем количестве вещей одновременно и лучше концентрироваться на проблеме. Тогда вы просто используете интерфейс службы (в C #):

// Notice the consuming class needs only know what it does, not how it does it
public interface IInvoicePublisher {
  pubic void Publish(Invoice anInvoice);
}

Это может быть реализовано различными способами, например:

public class DefaultPrinterInvoicePublisher
  DefaultPrinterInvoicePublisher _printer;
  public DefaultPrinterInvoicePublisher(DefaultPrinterFacade printer) {
    _printer = printer
  }
  public void Publish(Invoice anInvoice) {
    printableObject = //Generate crystal report, or something else that can be printed
    _printer.Print(printableObject);
  }

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

1 голос
/ 16 июля 2009

Я в целом согласен с комментариями Мехрдада и Муфасы. Там нет жесткого и быстрого правила для того, что лучше. Вы должны использовать подход, который подходит для конкретных сценариев, над которыми вы работаете, имея в виду:

  • удобочитаемость кода
  • чистота кода (может стать грязной, если вы передадите миллион и один параметр в метод, особенно если они являются переменными уровня класса. Альтернативой является инкапсуляция параметров в группы и создание, например, структуры для целых нескольких значений в одном объект)
  • тестируемость кода. Это важно на мой взгляд. Я иногда реорганизовывал код для добавления параметров в метод исключительно с целью улучшения тестируемости, поскольку это может позволить улучшить модульное тестирование
1 голос
/ 16 июля 2009

Как правило, лучше использовать параметры. Значительно расширяет возможности использования таких шаблонов, как внедрение зависимостей и дизайн, управляемый тестами.

Если это только внутренний метод, это не так важно.

0 голосов
/ 16 июля 2009

Я не передаю состояние объекта закрытым методам, потому что метод может обращаться к состоянию именно так.

Я передаю параметры частному методу, когда частный метод вызывается из открытого метода, и открытый метод получает параметр, который затем отправляет в частный метод.

Public DoTask( string jobid, object T)
{
 DoTask1(jobid, t);
 DoTask2(jobid, t);
}

private DoTask1( string jobid, object T)
{
}

private DoTask2( string jobid, object T)
{
}
...