Powtarzanie kodu a metoda wieloodpowiedzialna


11

Staram się przestrzegać zasady pojedynczej odpowiedzialności (SRP), a także pomijać powtórzenia kodu. Jednak często są miejsca, w których istnieją powtórzenia kodu, które są niczym więcej niż blokami kodu wywołań, które są odporne na wyodrębnienie ich do co najmniej sensownej nazwanej metody:

DoAction1();
DoAction2();

if (value)
    DoAction3();

DoAction4();

Jaki jest najlepszy sposób na wyodrębnienie takiego kodu do metody i jak go nazwać?


1
Dlaczego są odporne na ekstrakcję inną znaczącą metodą?
Chandranshu,

Wszystkie działania robią coś niezwiązanego. Musiałbym napisać: void MethodThatDoAction1ThenAction2AndAction3IfValueAndThenAction4(). Wolałbym zobaczyć większe znaczenie w: CodeBlock1().
yBee

4
To tylko kwestia prawidłowego nazwania wyodrębnionej metody, prawda? Obawiam się, że nie ma uniwersalnego rozwiązania i musisz to zrobić indywidualnie dla każdego przypadku. Jeśli powtarzasz ten sam fragment kodu w więcej niż 1 miejscu, twoje działania muszą mieć znaczenie i musisz się mocno przyjrzeć (może być dyskutowany), aby znaleźć to znaczenie i nadać mu nazwę.
Chandranshu,

1
Jeśli to jakaś pociecha, zwycięzca wiosenny dla długich nazw klas AbstractInterruptibleBatchPreparedStatementSetterjest tylko odrobinę mniejszy od twojej metody.
Chandranshu,

1
@Chandranshu Wierzę, że twój komentarz (y) działałby dobrze jako odpowiedź na to pytanie.
Simon Forsberg

Odpowiedzi:


13

Myślę, że to tylko kwestia prawidłowego nazwania wyodrębnionej metody. Obawiam się, że nie ma uniwersalnego rozwiązania i musisz to zrobić indywidualnie dla każdego przypadku. Jeśli powtarzasz ten sam fragment kodu w więcej niż 1 miejscu, twoje działania muszą mieć znaczenie i musisz się mocno przyjrzeć (może być dyskutowany), aby znaleźć to znaczenie i nadać mu nazwę.

Jeśli to jakaś pociecha, zwycięzcą wiosny dla długich nazw klas jest AbstractInterruptibleBatchPreparedStatementSetter49 znaków.


11

W rzeczywistości cierpisz na najtrudniejsze zadanie programisty - Naming Things . Przepraszam za tak zabawną odpowiedź, ale nie mogłem się oprzeć. Można zauważyć, że wiele osób cierpi z tego powodu , nawet w Internecie można znaleźć tego rodzaju esej .

Teraz, jeśli twój powtarzalny kod jest razem, co daje jedną pracę, a te metody nie są używane przez inne metody, to są one po prostu metodą pomocniczą, a następnie wynikową metodę można nazwać jako doXXXlub makeXXX... Myślę, że rozumiesz.

Jak powiedział Chandranshu „Jeśli powtarzasz to [...] znaczenie i nadajesz mu nazwę”. jest trafny i odpowiedni.

wprowadź opis zdjęcia tutaj

Zdjęcie dzięki uprzejmości: CodeChef Facebook Page Photo


2

Myślę, że przesadzasz z zasadą powtarzania kodu. Pomyśl o punkcie unikania powtarzania kodu. Chodzi o to, aby zmniejszyć ilość kodu, który należy sprawdzić, gdy nastąpi zmiana w logice, i zwiększyć zrozumienie, biorąc pod uwagę oczywiście podobnie zaprojektowane bloki.

Wadami faktoringu w celu uniknięcia powtórzeń jest to, że jeśli jeden ze współdzielonych bloków musi się zmienić, teraz potrzebujesz jeszcze bardziej złożonego dziedziczenia lub przełączania między implementacją standardową i niestandardową.

Ostrożnie rozważ więc możliwość zmiany logiki nawet jednego z tych bloków bez innych względem korzyści płynących ze zrozumienia uzyskanych dzięki uwzględnieniu tej podobieństwa. Jeśli jedna implementacja mogłaby oddzielić się od innych, lepiej byłoby po prostu powtórzyć kod.

Utrzymując ten powtarzający się kod, ponieważ staje się on bardziej złożony, a twoja domena problemowa staje się bardziej zdefiniowana, możesz wtedy uznać, że bardziej odpowiednie będzie uwzględnienie powtarzających się, teraz bardziej złożonych, ale także bardziej zdefiniowanych sekcji.

Zwykle staram się przez chwilę zachować identyczność edytora tekstu, dopóki nie zobaczę, czy coś, co wydaje się powtarzalne, okazuje się warte faktorowania. Po prostu powtarzam, ale nie spuszczam oka z przyszłości tego bloku, dzięki czemu łatwo później dopasować tekstowo.

Dużo czasu ta sama tożsamość i możliwy faktoring zaczynają się rozpraszać jako prawdziwe, kapryśne reguły biznesowe i wysoce zależna, często arbitralna logika; dodaje się, jak radzenie sobie z dziwactwami wielu typowych implementacji baz danych (ANSI_NULLS lub niektóre podobne) zmuszając coś, co wydawało się czystą logiką, do pokręconego bałaganu, próbując zapewnić rozsądną, dającą się obronić logikę decyzyjną w obliczu bałaganu panującego w branży.

Wydaje mi się, że gdyby ludzie próbowali oddzielić to, co próbujesz uwzględnić, mielibyśmy całą bibliotekę bezwartościowych konstrukcji, takich jak Do1Then2If2False Do1IfTrueDo2.

Musi być bardziej złożone i bardziej jasne, że blok nie zmieni się, aby uzasadnić faktoring.

To oprogramowanie . Możesz wrócić i edytować kilka takich samych bloków. Zajmie to 5 minut. I możesz zaoszczędzić godziny zmarnowanego faktoringu, a następnie godziny zmarnowanego dziedziczenia i przełączania rozwoju, po prostu go zostawiając i upewniając się, że masz dobrą klawiaturę przeciw RSI.


Podoba mi się część, która wyodrębnia istotę powtarzania kodu: aby zmniejszyć ilość kodu, który należy sprawdzić, gdy nastąpi zmiana w logice
yBee

1

Jeśli naprawdę przestrzegasz zasady pojedynczej odpowiedzialności, ten blok kodu musi mieć określony cel, prawda? W przeciwnym razie metody te byłyby w osobnych klasach.

Więc jaki jest cel tego bloku kodu? Ustal to i powinieneś wymyślić imię.

Jeśli nadal nie możesz znaleźć nazwy tej rzeczy, być może te metody w ogóle nie pasują do siebie. Tj. Ta klasa / blok kodu ma więcej niż jedną odpowiedzialność. W takim przypadku refaktoryzacja w celu podziału zadań może ujawnić lepszą nazwę ujawniającą cel.


1

Miałem sytuację, która może być podobna do twojej:

Istnieje klasa, która definiuje funkcjonalność reprezentowanego obiektu:

class Functionality
{
protected:
void functionA();
void functionB();
...
void functionZ();
}

Następnie istnieje klasa, która definiuje przepływy pracy dla operacji wysokiego poziomu wykonywanych przez obiekt:

class Workflows: private Functionality
{
    void WorkflowA()
    {
        functionA();

        if (m_bValue) {
            functionB();
        }

        functionC();
    }
    ...
    void WorkflowB();
}

Jeśli jesteś w podobnej sytuacji, określ, co reprezentują twoje klasy (w tym przypadku funkcjonalność / przepływy pracy), a następnie odpowiednio nazwij metody.

Oświadczenie: Nazwy klas użyte w tym przykładzie są rażąco niedokładne, ale nazwy metod dają wskazówkę. Zalecana dyskrecja.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.