Czy istnieją jakieś wady łączenia się w metody oparte na metodach referencyjnych?

14

Niedawno zasugerowałem metodę łączenia dla pewnej klasy w pewnym projekcie, aby poprawić czytelność kodu. Dostałem odpowiedź „płynne interfejsy nie powinny być implementowane tylko dla wygody, ale dla semantyki” i kazałem zrzucić moją sugestię. Odpowiedziałem, że nie sugeruję płynnego interfejsu, ale samą metodę łączenia łańcuchów (obie można pomylić ze sobą, czytać na dole), aby poprawić czytelność i komfort kodowania, sugestia została ponownie zestrzelona.

W każdym razie, to sprawiło, że pomyślałem, że być może będę narażony na złą praktykę, zawsze zwracając „to” metodami, które nie powinny niczego zwracać (np. Setery).

Moje pytanie brzmi: czy stosowanie poprzedniej konwencji można uznać za złą praktykę lub nadużycie ?, dlaczego ?. Nie sądzę, żeby były wady wydajności, czy są?

dukeofgaming
źródło
2
Spójrz na to pytanie, aby uzyskać miłą dyskusję programmers.stackexchange.com/questions/48419/...
KeesDijk
@KeesDijk Dzięki, edytowałem trochę moje pytanie, aby nie było podobne do tego, ponieważ bardziej interesuje mnie metoda łączenia łańcuchów z tej samej klasy
dukeofgaming
1
w SO powiedzieli mi, że tworzenie łańcuchów nie jest dla C ++ - stackoverflow.com/questions/5760551/… - o czym myślisz?
kagali-san
1
@mhambra Wygląda na to, że trzeba być ostrożnym przy wdrażaniu metod łączenia łańcuchów i zdefiniować jasne standardy dla tego.
dukeofgaming

Odpowiedzi:

10

Nie

Jak zauważa Kent Beck , kod jest odczytywany znacznie częściej niż jest pisany.

Jeśli łączenie metod sprawia, że ​​kod jest bardziej czytelny, użyj łączenia łańcuchowego metod.

Steven A. Lowe
źródło
1
Świetna książka .. +1
Rein Henrichs
5
ALE , co jeśli Twoja jedyna metoda wyszukiwania jest bardziej czytelna? Jeśli jest to kwestia sytilistyczna, będziesz trzymał się zestawu konwencji kodowania: oznacza to, że pisanie w taki sam sposób jak istniejący kod, dla zachowania spójności.
coredump
@coredump Wygląda na to, że OP już to powiedziano, ale myślę, że Steven właśnie to powiedział, jeśli łańcuchy metod sprawiają, że jest to bardziej czytelne.
Panzercrisis
1
@Panzercrisis OP otrzymało polecenie powstrzymania się od łączenia metod ze względu na wygodę kontra semantykę. Odpowiedź Stevena w zasadzie mówi „zrób to mimo wszystko” i rozważa jedynie czytelność kodu OP, bez uwzględnienia opinii zespołu lub spójności. Co jeśli OP chce przepisać każdą istniejącą klasę w projekcie, aby odpowiadała jego upodobaniom w zakresie czytelności kodu? Tak rozumiem argument wygody / semantyki: powinien istnieć „techniczny” powód, aby mieć łańcuch metod (przy okazji, ten argument brzmi również tak, jakby druga osoba
wymawiała
9

Tak, są wady

Kod, który jest łatwy do odczytania, jest dobry, ale należy także uważać na to, co kod komunikuje . Kiedy metody obiektu zawsze zwracają obiekt, komunikuje się kilka rzeczy:

  1. Potrzebuję zaawansowanej konfiguracji, która niekoniecznie jest oczywista, w jakiej kolejności należy ustawić lub skonfigurować rzeczy
  2. Każde kolejne wywołanie metody opiera się na ostatnim

Prawidłowy przypadek użycia: zapytania do bazy danych ad hoc

Biblioteki klas istnieją w większości języków, które pozwalają wyszukiwać w bazie danych bez uciekania się do SQL. Weźmy przykład Entity Framework dla platformy .NET:

DBContext db = new DBContext();
List<Post> posts = db.Posts
    .Where(post => post.Title.Contains("Test"))
    .OrderBy(post => post.DateCreated)
    .ToList();

Jest to płynny interfejs, w którym każde kolejne wywołanie metody opiera się na poprzednim. Logiczne czytanie tych wywołań ma sens w kontekście odpytywania bazy danych.

Nieprawidłowy przypadek użycia: cukier składniowy do ustawiania właściwości

Teraz zastosujmy ten sam wzór z Postklasą:

public class Post
{
    public string Title { get; set; }
    public DateTime DateCreated { get; set; }
    public string Body { get; set; }

    public Post SetTitle(string title)
    {
        Title = title;

        return this;
    }

    public Post SetDateCreated(DateTime created)
    {
        DateCreated = created;

        return this;
    }

    public Post SetBody(string body)
    {
        Body = body;

        return this;
    }
}

Spójrzmy teraz, jak używałbyś tej klasy:

Post post = new Post()
    .SetTitle("Test")
    .SetDateCreated(DateTime.Now)
    .SetBody("Just a test");

Kiedy widzę ten kod, natychmiast zadaję to pytanie: „Czy po wywołaniu SetBodywysyła zapytanie do bazy danych? Czy muszę wywoływać inną metodę, aby powiedzieć„ gotowe ”?

Co łańcuchowe wywołania metod komunikują się z kodem za pomocą Postklasy?

  1. Mam skomplikowaną konfigurację
  2. Każde wywołanie metody opiera się na poprzednim

Czy to rzeczywiście prawda? Nie. PostKlasa nie ma skomplikowanej konfiguracji. Ustawienie tytułu, daty utworzenia i treści nie opiera się na sobie w celu osiągnięcia bardziej skomplikowanego celu końcowego. Wcieliłeś kwadratowy kołek w okrągły otwór.

Wadą samodzielnego łączenia metod jest to, że komunikujesz, że do wykonania czegoś potrzeba wielu wywołań metod i że każde wywołanie opiera się na ostatnim. Jeśli nie jest to prawdą, wówczas łączenie metod może przekazywać niewłaściwe informacje innym programistom.

Kiedy twoi współpracownicy powiedzieli:

Płynne interfejsy nie powinny być implementowane tylko dla wygody, ale dla semantyki

Miały absolutną rację. Płynny interfejs lub łączenie metod komunikuje coś samo w sobie, co może nie być prawdą.

Greg Burghardt
źródło
Zakłada się, że łączenie metod i złożona konfiguracja zawsze idą w parze. Są odrębnymi bytami, a obecność jednego nie powinna skłonić cię do założenia drugiego.
Jack
To nie tylko „złożona konfiguracja”, ale także „kolejne wywołania metod oparte na poprzedniej”. Łańcuch metod zawiera meta-komunikację, która nie zawsze jest zgodna z celem klasy.
Greg Burghardt,
1
Kolejne wywołania metod odbiegające od poprzednich również różnią się od łańcuchów metod i nie sądzę, że traktowanie składni łańcuchów metod jako silnej komunikacji dla semantyki serii zależnych od porządku operacji jest niezawodne lub przydatne. To skrót składniowy, nic więcej. Używaj go tam, gdzie jest krótszy i bardziej czytelny.
Jack
1
Rozumiem, że uważasz, że tworzenie łańcuchów metod jest oddzielne, ale mówię, że inni programiści, którzy nie byli w twojej głowie i używają innych bibliotek klas, które implementują łańcuchy metod z tych powodów, mogą w końcu przyjmować założenia dotyczące twojego kodu, które są nieprawdziwe. Pamiętaj, że tworzenie łańcuchów metod może sugerować coś, czego ty, autor kodu, nie zamierzałeś. Kod, który komunikuje się wyraźnie, jest tak samo ważny jak kod łatwy do odczytania. Nie poświęcajcie się jedni za drugich.
Greg Burghardt,
1
When an object's methods always return the object, it communicates a couple of things- Myślę, że to osobista opinia; trudno twierdzić, że każdy, kto patrzy na powiązane metody, zakłada te same rzeczy.
Sam Dufel,
1

Główną wadą jest utrata przejrzystości. Na przykład:

x.foo();
x.bar();
x.baz();

Ponieważ żadna z tych instrukcji nie zwraca wartości (a jeśli tak, to jest ignorowana), mogą być użyteczne tylko wtedy, gdy wywołują skutki uboczne. Porównaj to z:

x.foo()
 .bar()
 .baz();

Po pierwsze, nie jest jasne bari bazdziała na obiektach tego samego typu co x, chyba że wiadomo, że xklasa jest jedyną klasą z tymi metodami. Nawet jeśli tak jest, nie jest jasne, czy metody działają na xnowych obiektach.

Podświetlanie części kodu, które mają skutki uboczne, jest przydatne, ponieważ trzeba śledzić te skutki uboczne, aby uzasadnić program. Musisz porównać potencjalną utratę przejrzystości z potencjalnym zyskiem na łatwości pisania kodu, ale pamiętaj również, że kod jest czytany więcej niż jest napisany.

Doval
źródło
0

Rozważ wszystkie elementy stylistyczne języków programowania (w przeciwieństwie do języka maszynowego kodowania ręcznego) i osłabia to argument „semantyka, a nie wygoda”.

Wiele z tego, co robimy jako inżynierowie, zapewnia wygodę w zakresie semantyki.

Obrabować
źródło