Вопрос по method-chaining, fluent-interface, oop – Метод цепочки - почему это хорошая практика или нет?

136

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

participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()

Кажется, это считается хорошей практикой, поскольку она создает читаемый код или «свободный интерфейс». Однако мне кажется, что вместо этого он нарушает нотацию вызова объекта, подразумеваемую самой ориентацией объекта - результирующий код не представляет выполнение действий надresult предыдущего метода, как обычно ожидается работа объектно-ориентированного кода:

participant.getSchedule('monday').saveTo('monnday.file')

Это различие позволяет создать два разных значения для точечной нотации «вызова результирующего объекта»: в контексте цепочки приведенный выше пример будет выглядеть как сохранениеparticipant объект, хотя этот пример на самом деле предназначен для сохранения объекта расписания, полученного getSchedule.

Я понимаю, что разница здесь в том, должен ли вызываемый метод возвращать что-то или нет (в этом случае он будет возвращать сам вызываемый объект для создания цепочки). Но эти два случая неотличимы от самой нотации, только от семантики вызываемых методов. Когда цепочка методов не используется, я всегда могу знать, что вызов метода работает с чем-то, связанным сresult предыдущего вызова - с цепочкой это предположение нарушается, и мне приходится семантически обрабатывать всю цепочку, чтобы понять, что на самом деле называется вызываемый объект. Например:

participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))

Там последние два вызова метода относятся к результату getSocialStream, тогда как предыдущие вызовы относятся к участнику. Может быть, это плохая практика - писать цепочки с изменяющимся контекстом (не так ли?), Но даже тогда вам придется постоянно проверять, действительно ли точечные цепочки, которые выглядят одинаково, соответствуют одному и тому же контексту или работают только над ними. результат.

Мне кажется, что, хотя поверхностное сцепление методов создает читабельный код, перегрузка значения точечной нотации только приводит к еще большей путанице. Поскольку я не считаю себя гуру программирования, я полагаю, что вина моя.So: What am I missing? Do I understand method chaining somehow wrong? Are there some cases where method chaining is especially good, or some where it's especially bad?

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

Кажется, что цепочка методов - это как минимум один из способов написания умного кода на Java. Даже если не все согласны .. Mercer Traieste
Они также называют их "свободно" методы или «свободный» интерфейс. Вы можете обновить свой заголовок, чтобы использовать этот термин. S.Lott
В другом обсуждении SO было сказано, что свободный интерфейс - это более масштабная концепция, связанная с читабельностью кода, и объединение методов - только один из способов достижения этой цели. Однако они тесно связаны, поэтому я добавил теги и ссылки на свободно распространяемые интерфейсы в тексте - я думаю, что этого достаточно. Ilari Kajaste
Это отличная идиома кодирования, но, как и все отличные инструменты, ею злоупотребляют. Martin Spamer
То, как я об этом подумал, заключается в том, чтоmethod chaining is, in fact, a hackaround to get a missing feature into the language syntax. Это действительно не нужно, если есть встроенная альтернативная запись. это игнорировало бы любые возвращаемые значения метода и всегда вызывало бы любые цепочечные методы, использующие один и тот же объект. Ilari Kajaste

Ваш Ответ

16   ответов
6

Benefits of Chaining

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

Я знаю, что это надуманный пример, но скажем, у вас есть следующие классы

Public Class Location
   Private _x As Integer = 15
   Private _y As Integer = 421513

   Public Function X() As Integer
      Return _x
   End Function
   Public Function X(ByVal value As Integer) As Location
      _x = value
      Return Me
   End Function

   Public Function Y() As Integer
      Return _y
   End Function
   Public Function Y(ByVal value As Integer) As Location
      _y = value
      Return Me
   End Function

   Public Overrides Function toString() As String
      Return String.Format("{0},{1}", _x, _y)
   End Function
End Class

Public Class HomeLocation
   Inherits Location

   Public Overrides Function toString() As String
      Return String.Format("Home Is at: {0},{1}", X(), Y())
   End Function
End Class

И скажем, у вас нет доступа к базовому классу, или Скажите, что значения по умолчанию являются динамическими, основанными на времени и т. Д. Да, вы можете создать экземпляр, затем изменить значения, но это может стать громоздким, особенно если вы просто передаете значения для метода:

  Dim loc As New HomeLocation()
  loc.X(1337)
  PrintLocation(loc)

Но это не намного легче читать:

  PrintLocation(New HomeLocation().X(1337))

Или как насчет ученика?

Public Class Dummy
   Private _locA As New Location()
   Public Sub New()
      _locA.X(1337)
   End Sub
End Class

против

Public Class Dummy
   Private _locC As Location = New Location().X(1337)
End Class

Вот как я использовал цепочку, и обычно мои методы предназначены только для конфигурации, поэтому они имеют длину всего 2 строки, задают значение, затемReturn Me, Для нас это вычистило огромные строки, которые очень трудно читать и понимать, в одну строку, которая читается как предложение. что-то вроде

New Dealer.CarPicker().Subaru.WRX.SixSpeed.TurboCharged.BlueExterior.GrayInterior.Leather.HeatedSeats

Что-то вроде

New Dealer.CarPicker(Dealer.CarPicker.Makes.Subaru
                   , Dealer.CarPicker.Models.WRX
                   , Dealer.CarPicker.Transmissions.SixSpeed
                   , Dealer.CarPicker.Engine.Options.TurboCharged
                   , Dealer.CarPicker.Exterior.Color.Blue
                   , Dealer.CarPicker.Interior.Color.Gray
                   , Dealer.CarPicker.Interior.Options.Leather
                   , Dealer.CarPicker.Interior.Seats.Heated)

Detriment Of Chaining
то есть там, где я не хотел бы его использовать

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

Также существует опасение, что подпрограмма будет возвращать недействительные данные, поэтому до сих пор я использовал цепочку, только когда я возвращал тот же самый вызываемый экземпляр. Как указывалось, если вы создаете цепочку между классами, вы усложняете отладку (которая вернула ноль?) И можете увеличить связь между классами.

Conclusion

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

Я стараюсь следовать этим правилам.

Try not to chain between classes Make routines specifically for chaining Do only ONE thing in a chaining routine Use it when it improves readability Use it when it makes code simpler
EEEEEEWWWW !!!!!! Visual Basic! Сырые машинные коды более понятны, чем эта хрень.
хаха, согласился. К сожалению, это то, что моя работа решила использовать.
6

DSL, в Java напрямую. По сути, вы можете смоделировать по крайней мере следующие типы правил DSL:

1. SINGLE-WORD
2. PARAMETERISED-WORD parameter
3. WORD1 [ OPTIONAL-WORD]
4. WORD2 { WORD-CHOICE-A | WORD-CHOICE-B }
5. WORD3 [ , WORD3 ... ]

Эти правила могут быть реализованы с использованием этих интерфейсов

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow for
// repetitions. Repetitions can be ended any time because this interface
// extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

С помощью этих простых правил вы можете реализовать сложные DSL, такие как SQL, непосредственно в Java, как это делаетсяjOOQбиблиотека, которую я создал. Смотрите довольно сложный пример SQL, взятый измой блог Вот:

create().select(
    r1.ROUTINE_NAME,
    r1.SPECIFIC_NAME,
    decode()
        .when(exists(create()
            .selectOne()
            .from(PARAMETERS)
            .where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
            .and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
            .and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
                val("void"))
        .otherwise(r1.DATA_TYPE).as("data_type"),
    r1.NUMERIC_PRECISION,
    r1.NUMERIC_SCALE,
    r1.TYPE_UDT_NAME,
    decode().when(
    exists(
        create().selectOne()
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
        create().select(count())
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
    .as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch()

Еще один хороший примерjRTFнемного DSL, предназначенный для создания RTF-документов непосредственно в Java. Пример:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );
Хороший трюк. Я думаю, это может быть использовано в C ++ и C #.
@ user877329: Да, его можно использовать практически в любом объектно-ориентированном языке программирования, который знает что-то вроде интерфейсов и полиморфизма подтипов.
3

ев, но я думаю, что оно имеет место. Один пример может быть найден вИспользование активной записи CodeIgniter:

$this->db->select('something')->from('table')->where('id', $id);

Это выглядит намного чище (и, на мой взгляд, более разумно), чем:

<,pre>$this->db->select('something'); $this->db->from('table'); $this->db->where('id', $id);

Это действительно субъективно; Каждый человек имеет свое собственное мнение.

Это пример свободного интерфейсаwith Метод Chaining, поэтому UseCase здесь немного отличается. Вы не просто создаете цепочку, а создаете язык, специфичный для внутреннего домена, который легко читается. В отношении идентификатора CI ActiveRecord не является ActiveRecord.
37

которые действуют только на исходный объект, например установка нескольких свойств или вызов методов утилитарного типа.

foo.setHeight(100).setWidth(50).setColor('#ffffff');
foo.moveTo(100,100).highlight();

Я не использую его, когда один или несколько связанных методов вернут в моем примере любой объект, кроме foo. Несмотря на то, что синтаксически вы можете связывать что угодно, если вы используете правильный API для этого объекта в цепочке, изменение объектов IMHO делает вещи менее читабельными и может привести к путанице, если API для разных объектов имеют какие-либо сходства. Если вы делаете какой-то действительно общий вызов метода в конце (.toString(), .print()что бы) на какой объект вы в конечном итоге воздействуете? Кто-то, случайно читающий код, может не заметить, что это будет неявно возвращаемый объект в цепочке, а не исходная ссылка.

Цепочка различных объектов также может привести к неожиданным ошибкам. В моих примерах, предполагая, чтоfoo допустимо, все вызовы методов являются «безопасными» (например, действительно для foo). В примере OP:

participant.getSchedule('monday').saveTo('monnday.file')

... как сторонний разработчик, смотрящий на код, нет никакой гарантии, что getSchedule на самом деле вернет действительный ненулевой объект расписания. Кроме того, отладка этого стиля кода часто намного сложнее, так как многие IDE не будут оценивать вызов метода во время отладки как объект, который вы можете проверить. IMO, всякий раз, когда вам может понадобиться объект для проверки в целях отладки, я предпочитаю иметь его в явной переменной.

Если есть шанс, чтоParticipant не имеет действительногоSchedule затемgetSchedule Метод предназначен для возвратаMaybe(of Schedule) тип иsaveTo Метод предназначен для принятияMaybe тип.
+1 за поднятие вопроса о безопасном методе цепочки.
70

что это субъективно. По большей части я избегаю цепочки методов, но недавно я также нашел случай, когда это было просто правильно - у меня был метод, который принимал около 10 параметров и требовал большего, но в большинстве случаев вам нужно было только указать мало. С переопределениями это стало очень громоздким и очень быстрым. Вместо этого я выбрал подход цепочки:

MyObject.Start()
    .SpecifySomeParameter(asdasd)
    .SpecifySomeOtherParameter(asdasd)
    .Execute();

Это что-то вроде заводского образца. Подход цепочки методов был необязательным, но он облегчал написание кода (особенно с IntelliSense). Имейте в виду, что это один изолированный случай, и он не является общей практикой в моем коде.

Дело в том, что в 99% случаев вы, вероятно, можете делать то же самое или даже лучше без цепочки методов. Но есть 1%, где это лучший подход.

IMO, лучший способ использовать цепочку методов в этом сценарии - создать объект параметра для передачи в функцию, напримерP = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);, У вас есть преимущество в возможности повторного использования этого объекта параметра в других вызовах, что является плюсом!
Просто мой вклад в модели, фабричный метод обычно имеет только одну точку создания, и продукты выбираются статически, основываясь на параметрах фабричного метода. Создание цепочки больше похоже на шаблон Builder, где вы вызываете различные методы для получения результата, методы могут быть необязательными, как вMethod chainingмы можем иметь что-то вродеPizzaBuilder.AddSauce().AddDough().AddTopping() больше ссылокhere
Я знаю, что ты имеешь в виду, Вилкс. Но все же, когда я читаюlist.add(someItem)Я прочитал это как & quot; этот код добавляетsomeItem кlist объект & Quot ;. поэтому, когда я читаюPizzaBuilder.AddSauce()Я прочитал это естественно как & quot; этот код добавляет соус кPizzaBuilder объект & Quot ;. Другими словами, я вижу оркестратор (который выполняет добавление) как метод, в котором кодlist.add(someItem) или жеPizzaBuilder.addSauce() встроен Но я просто думаю, что пример PizzaBuilder немного искусственный. Ваш примерMyObject.SpecifySomeParameter(asdasd)прекрасно работает для меня
@Marco Medrano Пример PizzaBuilder всегда меня раздражал, так как я читал его в JavaWorld давным-давно. Я чувствую, что должен добавить соус к моей пицце, а не к своему шеф-повару.
Метод цепочки (как показано в оригинальном вопросе) считается плохим, когда он нарушаетthe law of demeter, Увидеть:ifacethoughts.net/2006/03/07/… Ответ, данный здесь, фактически следует этому закону, поскольку он является «шаблоном строителя».
1

Абсолютно упущенный момент здесь заключается в том, что метод цепочки позволяетDRY, Это эффективный заменитель для "с" (который плохо реализован на некоторых языках).

A.method1().method2().method3(); // one A

A.method1();
A.method2();
A.method3(); // repeating A 3 times

Это имеет значение по той же причине, что DRY всегда имеет значение; если A оказывается ошибкой, и эти операции необходимо выполнить на B, вам нужно обновить только в 1 месте, а не в 3.

С практической точки зрения преимущество в этом случае невелико. Тем не менее, немного меньше печатать, немного более устойчивый (СУХОЙ), я возьму это.

Повторение имени переменной в исходном коде не имеет ничего общего с принципом СУХОЙ. DRY заявляет, что «каждая часть знаний должна иметь одно, однозначное, авторитетное представление в системе» или, другими словами, избегать повторенияknowledge (не повторениеtext).
Это, безусловно, повторение, которое нарушает сухой. Повторение имен переменных (без необходимости) причиняет все зло точно так же, как это делают другие формы «сухого»: оно создает больше зависимостей и больше работы. В приведенном выше примере, если мы переименуем A,wet Версия будет нуждаться в 3 изменениях и вызовет ошибки для отладки, если пропущены какие-либо из трех.
& Quot; компилятор & quot; может вернуть ошибку, или, возможно, вы используете PHP или JS, и там интерпретатор может выдавать ошибку только во время выполнения, когда вы достигаете этого условия.
Я не вижу проблемы, которую вы указали в этом примере, поскольку все вызовы методов близки друг к другу. Кроме того, если вы забудете изменить имя переменной в одной строке, компилятор вернет ошибку, и это будет исправлено, прежде чем вы сможете запустить вашу программу. Кроме того, имя переменной ограничено областью ее объявления. Если эта переменная не является глобальной, что уже является плохой практикой программирования. ИМО, СУХОЙ - это не меньше печатать, а держать вещи в изоляции.
3

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

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

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

Также это:

convertTextToVoice.LoadText("source.txt").ConvertToVoice("destination.wav");

Вот как я бы обычно использовал это. Я использую это, чтобы связать число параметров по x, как я обычно это использую. Если бы я хотел ввести х число параметров в вызове метода, я бы использовалparams синтаксис:

public void foo(params object[] items)

И приведите объекты на основе типа или просто используйте массив или коллекцию типов данных в зависимости от вашего варианта использования.

+1 на & quot; основная ошибка думает, что этоobject oriented подход в целом, когда на самом деле это большеfunctional programming Подход, чем все остальное ". Известный вариант использования предназначен для манипуляций без состояния на объектах (где вместо изменения его состояния вы возвращаете новый объект, с которым продолжаете действовать). Вопросы OP и другие ответы показывают действия с состоянием, которые действительно кажутся неловкими с цепочкой.
8

потому что вы можете зависеть от большего количества объектов, чем ожидалось, как тогда ваш вызов возвращает экземпляр другого класса:

Я приведу пример:

foodStore - это объект, который состоит из множества ваших продуктовых магазинов. foodstore.getLocalStore () возвращает объект, который содержит информацию о ближайшем хранилище к параметру. getPriceforProduct (что угодно) - это метод этого объекта.

Поэтому, когда вы вызываете foodStore.getLocalStore (параметры) .getPriceforProduct (что угодно)

Вы зависите не только от FoodStore, но и от LocalStore.

Если getPriceforProduct (что-либо) когда-либо изменится, вам нужно изменить не только FoodStore, но и класс, который вызвал цепочечный метод.

Вы всегда должны стремиться к слабой связи между классами.

Это, как говорится, мне лично нравится цеплять их при программировании Ruby.

0

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

1.)

participant
    .addSchedule(events[1])
    .addSchedule(events[2])
    .setStatus('attending')
    .save();

против

participant.addSchedule(events[1]);
participant.addSchedule(events[2]);
participant.setStatus('attending');
participant.save()

2.)

participant
    .getSchedule('monday')
        .saveTo('monnday.file');

против

mondaySchedule = participant.getSchedule('monday');
mondaySchedule.saveTo('monday.file');

3.)

participant
    .attend(event)
    .setNotifications('silent')
    .getSocialStream('twitter')
        .postStatus('Joining '+event.name)
        .follow(event.getSocialId('twitter'));

против

participant.attend(event);
participant.setNotifications('silent')
twitter = participant.getSocialStream('twitter')
twitter.postStatus('Joining '+event.name)
twitter.follow(event.getSocialId('twitter'));

Как вы можете видеть, вы выигрываете почти ничего, потому что вам нужно добавить разрывы строк к вашему единственному выражению, чтобы сделать его более читабельным, и добавить отступ, чтобы было ясно, что вы говорите о разных объектах. Ну, если бы я хотел использовать язык на основе идентификаторов, то вместо этого я бы изучал Python, не говоря уже о том, что большинство IDE удаляют отступы путем автоматического форматирования кода.

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

Что касается СУХОЙ интерпретации: «Избегайте повторения знаний (не повторения текста)». и «Введите меньше, даже не повторяйте тексты». Первый - то, что на самом деле означает принцип, но второй - это общее недоразумение, потому что многие люди не могут понять слишком сложную чушь, как «Каждая часть знаний должна иметь один однозначное, авторитетное представление в системе ". Второй - это компактность любой ценой, что нарушает этот сценарий, поскольку ухудшает читаемость. Первая интерпретация нарушается DDD, когда вы копируете код между ограниченными контекстами, потому что слабая связь более важна в этом сценарии.

0

It's terse, yet allows you to put more into a single line elegantly. You can sometimes avoid the use of a variable, which may occasionally be useful. It may perform better.

Плохо:

You're implementing returns, essentially adding functionality to methods on objects that isn't really a part of what those methods are meant to do. It's returning something you already have purely to save a few bytes. It hides context switches when one chain leads to another. You can get this with getters, except it's pretty clear when the context switches. Chaining over multiple lines looks ugly, doesn't play well with indentation and can cause some operator handling confusion (especially in languages with ASI). If you want to start returning something else that's useful for a chained method, you're potentially going to have a harder time fixing it or hit more problems with it. You're offloading control to an entity that you wouldn't normally offload to purely for convenient, even in strictly typed languages mistakes caused by this cannot always be detected. It may perform worse.

Генеральный:

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

В некоторых случаях сцепление может сильно ухудшить читабельность, особенно при взвешивании в точках 1 и 2.

Применительно к этому может быть использовано не по назначению, например, вместо другого подхода (например, передача массива) или смешивания методов причудливыми способами (parent.setSomething (). GetChild (). SetSomething (). GetParent (). GetSomething ()).

6

а не для удобства чтения. Цепочка методов приемлема, если она включает в себя выполнение одного и того же действия с одним и тем же объектом, но только если она действительно улучшает читабельность, а не только для написания меньшего количества кода.

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

73

Метод цепочки делает отладку сложной:  - Вы не можете поместить точку останова в краткую точку, чтобы вы могли приостановить программу именно там, где вы хотите.  - Если один из этих методов выдает исключение, и вы получаете номер строки, вы не знаете, какой метод в & quot; цепочке & quot; вызвал проблему.

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

РЕДАКТИРОВАТЬ: комментарий упоминает, что методы цепочки и переносы строк являются отдельными. Это правда. Однако в зависимости от отладчика может быть или не быть возможности разместить точку останова в середине оператора. Даже если вы можете, использование отдельных строк с промежуточными переменными дает вам гораздо большую гибкость и целый набор значений, которые вы можете просмотреть в окне Watch, что помогает процессу отладки.

+1, безо всякого интереса, во времени вы знаете, что возвращал каждый метод при пошаговой отладке цепочки методов. Шаблон цепочки методов - это хак. Это худший кошмар программиста по обслуживанию.
Этот ответ полностью оправдан, но он просто показывает слабость, присутствующую во всех известных мне отладчиках, и не имеет отношения конкретно к этому вопросу.
Если все классы, использующие цепочку, реализуют такой интерфейс, какinterface Chainable { Chainable chainID(Object o_cid); Object getChainID(); }, тогда очень легко определить, где произошла ошибка. Однако он должен быть заложен в основу вашего кода.
@Brabster. Они могут быть отдельными для определенных отладчиков, однако наличие отдельных вызовов с промежуточными переменными все же дает вам гораздо больший объем информации, пока вы исследуете ошибки.
Разве не используются независимые переводы строк и методов? Вы можете связывать каждый вызов по новой строке в соответствии с @ Vilx-answer, и обычно вы можете поместить несколько отдельных операторов в одну строку (например, используя точки с запятой в Java).
23

Method Chaining

When to use it

Method Chaining can add a great deal to the readability of an internal DSL and as a result has become almost a synonum for internal DSLs in some minds. Method Chaining is best, however, when it's used in conjunction with other function combinations.

Method Chaining is particularly effective with grammars like parent::= (this | that)*. The use of different methods provides readable way of seeing which argument is coming next. Similarly optional arguments can be easily skipped over with Method Chaining. A list of mandatory clauses, such as parent::= first second doesn't work so well with the basic form, although it can be supported well by using progressive interfaces. Most of the time I'd prefer Nested Function for that case.

The biggest problem for Method Chaining is the finishing problem. While there are workarounds, usually if you run into this you're better off usng a Nested Function. Nested Function is also a better choice if you are getting into a mess with Context Variables.

Ссылка мертва :(
6

Метод цепочки не является чем-то плохим или хорошим по своей сути.

Читаемость это самая важная вещь.

(Также учтите, что большое количество методов в цепочке сделает вещи очень хрупкими, если что-то изменится)

Это действительно может быть субъективным, следовательно, субъективный тег. Надеюсь, ответы на эти вопросы подчеркнут меня в тех случаях, когда объединение методов было бы хорошей идеей - сейчас я не вижу ничего, но я думаю, что это просто моя неспособность понять хорошие моменты концепции, а не что-то изначально плохое в самой цепочке. Ilari Kajaste
Разве это не было бы плохо по своей природе, если бы это привело к сильной связи? Разбиение цепочки на отдельные операторы не снижает читабельность.
зависит от того, насколько догматично вы хотите быть. Если это приводит к чему-то более читабельному, то это может быть предпочтительнее во многих ситуациях. Большая проблема, с которой я сталкиваюсь при таком подходе, состоит в том, что большинство методов объекта возвращают ссылки на сам объект, но часто метод возвращает ссылку на дочерний объект, с которым можно связать несколько методов. Как только вы начинаете это делать, другому программисту становится очень трудно распутать происходящее. Также любое изменение в функциональности метода будет болезненным для отладки в большом составном операторе.
20

метод цепочки является чем-то новым. Конечно, это выглядит круто, но я не вижу в этом никаких реальных преимуществ.

Как:

someList.addObject("str1").addObject("str2").addObject("str3")

лучше чем:

someList.addObject("str1")
someList.addObject("str2")
someList.addObject("str3")

Исключением может быть ситуация, когда addObject () возвращает новый объект, и в этом случае код без цепочки может быть немного более громоздким, например:

someList = someList.addObject("str1")
someList = someList.addObject("str2")
someList = someList.addObject("str3")
Хорошие замечания о читаемости и принципах СУХО с оговоркой, что цепочка методов предотвращает самоанализ возвращаемого значения данного метода и / или предполагает презумпцию пустых списков / наборов и ненулевых значений от каждого метода в цепочке (ошибка, приводящая к ошибкам многие NPE в системах приходится часто отлаживать / исправлять).
@ChrisDodd Никогда не слышал об автозаполнении?
@ inf3rno: Как автозаполнение помогает читабельности? Если что-то иное - позволяет легко писать длинные нечитаемые путаницы, которые может понять только компьютер.
Реальное преимущество наличия «someList» только один раз будет гораздо проще дать ему более длинное и описательное имя. В любое время, когда имя должно появляться несколько раз в быстрой последовательности, существует тенденция сокращать его (сокращать повторения и улучшать читаемость), что делает его менее описательным, что ухудшает читабельность.
Это более кратко, поскольку вы избегаете двух из «someList»; части даже в вашем первом примере и заканчиваются одной строкой вместо трех. Теперь, если это на самом деле хорошо или плохо, зависит от разных вещей и, возможно, дело вкуса.
2

я изменил способ, которым свободный интерфейс был реализован в моей библиотеке.

До:

collection.orderBy("column").limit(10);

После:

collection = collection.orderBy("column").limit(10);

В разделе «до» Реализация функций изменила объект и закончиласьreturn this. I changed the implementation to return a new object of the same type.

My reasoning for this change:

The return value had nothing to do with the function, it was purely there to support the chaining part, It should have been a void function according to OOP.

Method chaining in system libraries also implement it that way (like linq or string):

myText = myText.trim().toUpperCase();

The original object remains intact, allowing the API user to decide what to do with it. It allows for:

page1 = collection.limit(10);
page2 = collection.offset(10).limit(10);

A copy implementation can also be used for building objects:

painting = canvas.withBackground('white').withPenSize(10);

Where the setBackground(color) function changes the instance and returns nothing (like its supposed to).

The behavior of the functions are more predicable (See point 1 & 2).

Using a short variable name can also reduce code-clutter, without forcing a api on the model.

var p = participant; // create a reference
p.addSchedule(events[1]);p.addSchedule(events[2]);p.setStatus('attending');p.save()

Conclusion:
На мой взгляд, свободный интерфейс, который используетreturn this реализация просто неправильно.

Мой PHP Framework:github.com/bfanger/sledgehammer_core
@ Apeiron Производительность определенно быстрее, чемreturn this удалось или иначе. Это мое мнение, что он получает плавный API в «неестественном». путь. (Добавленная причина 6: показать альтернативную версию, которая не имеет служебных / дополнительных функций)
Я с тобой согласен. В большинстве случаев лучше оставить базовое состояние DSL нетронутым и возвращать новые объекты при каждом вызове метода, вместо этого ... Мне любопытно: что за библиотеку вы упомянули?
Но разве возвращение нового экземпляра для каждого вызова не создаст много накладных расходов, особенно если вы используете более крупные элементы? По крайней мере, для неуправляемых языков.

Похожие вопросы