Kiedy przenieść wspólne pole do klasy podstawowej?

16

Obecnie mam dwie klasy pochodzące, Ai B, że obaj mają równe wspólnego i próbuję ustalić, czy należy przejść do klasy podstawowej.

Nigdy nie jest przywoływany z klasy podstawowej i powiedzmy, że jeśli w pewnym momencie drogi zostanie wyprowadzona inna klasa C, która nie ma _field1, to nie naruszono by zasady „najmniej uprzywilejowanego” (lub czegoś), gdyby było?

public abstract class Base
{
    // Should _field1 be brought up to Base?
    //protected int Field1 { get; set; }
}

public class A : Base
{
    private int _field1;
}

public class B : Base
{
    private int _field1;
}

public class C : Base
{
    // Doesn't have/reference _field1
}
Sam jest
źródło
18
Myślę, że to pytanie jest jasne, bo nie dały nam jakiś pomysł co Base, A, B, C, i _field1są. To ważne szczegóły, których nie należy pomijać; Myślę, że powinieneś edytować pytanie, aby porozmawiać o tym, co to jest.
Tanner Swett
W oparciu o odpowiedzi chciałbym: przebudować Pojazd, aby mieć wirtualne Zawieszenie, następnie Samochód i Rower mogą mieć Koła, a Łódź może mieć Pływalność, która przesunie abstrakcję w górę. Uważam, że jeśli moja abstrakcja prowadzi bezpośrednio do określonych ustawień, to nie przesunąłem koncepcji wystarczająco daleko w górę łańcucha.
Patrick Hughes
6
Nie używaj dziedziczenia klas, aby uniknąć powielania kodu. Użyj go do dziedziczenia i przedłużania zachowań , tj. Polimorfizmu. Przenieś wspólne pole do klasy bazowej wtedy i tylko wtedy , gdy jest to logicznie to samo pole, a nie dwie niepowiązane ze sobą informacje, które akurat dzielą tę samą nazwę w odpowiednich kontekstach.
Brandon
Dlaczego masz na początek klasę podstawową?
jpmc26

Odpowiedzi:

34

Wszystko zależy od dokładnego problemu, który próbujesz rozwiązać.

Rozważ konkretny przykład: twoja abstrakcyjna klasa podstawowa to Vehiclei masz obecnie konkretne implementacje Bicyclei Car. Jesteś rozważa przeniesienie numberOfWheelsod Bicyclei Cardo pojazdu. Czy powinieneś to zrobić? Nie! Ponieważ nie wszystkie pojazdy mają koła. Możesz już powiedzieć, że jeśli spróbujesz dodać Boatklasę, to się zepsuje.

Teraz, jeśli twoja abstrakcyjna klasa bazowa była WheeledVehicle, logiczne jest, aby mieć tam numberOfWheelszmienną składową.

Musisz zastosować tę samą logikę do swojego problemu, ponieważ, jak widać, nie jest to prosta odpowiedź tak lub nie.

Pete
źródło
3
Można tymczasowo zaakceptować fakt, że 0 jest prawidłową liczbąObrazów. Jednak w końcu możesz dodać roll()metodę, w której idea podklasy wygląda proroczo.
user949300
11
Łódź ma 0 kół. Co to psuje?
D Drmmr
14
@DDrmmr To nie tak, że Łódź ma 0 kół, to, że Koła nawet nie istnieją jako koncepcja Łodzi - dlatego twoje modele nie powinny na to pozwolić.
Peter M
10
Chodzi mi o to, że przykład jest zły. Nie ma nic koncepcyjnie złego w pojeździe (którym jest łódka) stwierdzającym, że ma 0 kół.
D Drmmr
10
Potem wszystko idzie do diabła, kiedy trzeba przechowywać wiosło.
IllusiveBrian
13

Logicznie rzecz biorąc, poza umieszczeniem pola replikowanego w podklasach vs. wspólnych w klasie bazowej istnieje trzecia opcja: wprowadzenie nowej podklasy do hierarchii, która ma wspólne właściwości między nimi. @ Pet zawiera podpowiedzi na ten temat, nie w pełni tam idąc.

Korzystając z przykładu @ Pete'a, wprowadzilibyśmy (prawdopodobnie abstrakcyjną) podklasę dla pojazdu kołowego, która pochodzi z oryginalnej klasy podstawowej - podczas gdy dwie podklasy z niej schodzą. Tak więc oryginalna klasa podstawowa nie jest zanieczyszczona kołami, jednak powszechność kół to OSUSZANIE (nie powtarzane wśród podklas, które mają koła).

Może to oczywiście przesadzić z twoimi celami, ale jest to obsługiwane przez mechanizm hierarchii klas.

Erik Eidt
źródło
Właśnie tak postanowiłem (A i B w większości się pokrywają, więc B zacznie od A).
samis
9

Będę tu grał adwokata diabła.

W tej chwili nie powinieneś nic robić .

Czy to jest SUCHE? Nie. Ale lepiej jest mieć odrobinę duplikacji niż przedwczesną abstrakcję, której później nie można łatwo wycofać. Refaktor do przeniesienia właściwości do wspólnej klasy podstawowej jest łatwy. Nie jest inaczej. Poczekaj i zobacz.

Podejmując taką decyzję, staram się stosować „zasadę 3”: kiedy powtórzę to samo, np. W trzech różnych miejscach, i dopiero wtedy rozważam przeniesienie go w górę łańcucha. Uwaga: masz tylko 2 lata.

Jared Smith
źródło
2
Mądre spostrzeżenie jest, powiedziałbym, wymagane do podjęcia decyzji.
radarbob
1
W większości się zgadzam, ale „W tej chwili” (bez dodatkowego kodu jak ten, który widzimy) refaktoryzacja w obu kierunkach jest banalna. Ale jeśli istnieje dodatkowy kod, który korzysta z pola, refaktoryzacja w obu kierunkach może stać się znacznie trudniejsza.
Doc Brown
2

Ogólnie rzecz biorąc, przeniósłbym go do klasy podstawowej. Nie sądzę, aby istniał obiektywny tak / nie, ponieważ istnieje tutaj kompromis - przenoszenie nieużywanych pól vs zmniejszanie złożoności.

Zwykle wolę „ciężkie” klasy podstawowe, które zawierają wszystko, co może być udostępniane. To sprawia, że ​​serializowanie do plików jest prostsze, ponieważ nie potrzebujesz potomnych metod serializacji w każdej klasie pochodnej. Ale jeśli nie masz tego lub podobnego problemu, a może musisz zrobić wszystko, co możliwe, aby zmniejszyć zużycie pamięci, wtedy pozostawienie tylko tych pól, które są potrzebne, powinno być w porządku.

Klasa „pośrednia”, która wprowadza wspólne pola, będzie dobrze, jeśli masz bardzo ograniczoną liczbę pól. Pamiętaj jednak, że takie podejście może znacznie zwiększyć złożoność, jeśli masz dziesiątki pól używanych w różnych kombinacjach, co prowadzi do wielu klas pośrednich, z których każda wprowadza określony zestaw pól. To może stać się problemem konserwacyjnym.

Grandmaster B.
źródło
Mój przykład jest trywialny, choć nadal jest kodem produkcyjnym. Dobry argument przemawia za „skompromitowaniem” hierarchii „ciężką” klasą podstawową, do której i tak skłoniłbym się w takim przypadku.
samis
1
@samis: używasz klas o nazwach „A, B, C” i „Base” za pomocą tylko jednego pola i żadnej metody w kodzie produkcyjnym? Kwestionuję to.
Doc Brown