Odpowiednia "konwersja" czy możliwe?

0

Witam. Zaczynam dopiero programowanie w Javie. Piszę coś własnego dokładając kolejne klocki jeśli poznam coś nowego. Ale nie w tym problem.
Powiedzmy, że robię sobie mini projekcik związany z samochodami. Mam jedną klasę jako ogólnie jakiś tam pojazd, samochód w którym znajdują się różne rzeczy, w tym obiekty innych klas (np. zawieszenie, silnik itp.). Jak wiadomo każdy samochód ma różny silnik, typ silnika itp. Stworzyłem sobie klasę abstrakcyjną Silnik gdzie są ogólne dane o silniku. Potem rozszerzałem je o kolejne np. Diesla-> Turbo Diesla, Benzynowy -> Turbo Benzyna, coś w tym stylu. Z "bazy" z pliku wczytuję sobie dane o konkretnych silnikach gdzie są wczytywane do programu. W zależności od tego jaki się wylosował to chciałbym go dorzucić do jakiegoś tam samochodu.
Problem jest taki, że nie mam pomysłu, nie wiem, jak zrobić by dodając do tej uniwersalnej klasy samochodu móc korzystać z różnych "silników". Gdy tam stworzę sobie obiekt klasy Silnik to odlecą mi metody do obsługi podklas (np związane z cechami charakterystycznymi różnych typów silników). W jaki sposób zrobić taką jakby konwersję by obiekt w klasie był uniwersalny i czy w ogóle jest coś takiego możliwe?
Z góry dziękuję za pomoc :)

0

Jeżeli dobrze zrozumiałem, to dodaj do auta zamiast pole z "Silniki Diesel" sam "silnik", wtedy powinieneś móc "zmieniać" silniki. Ew zrób jakiś interfejs którego będą implementowac wszystkie silniki i jako pole daj zmienną klasy interfejsu (np. NazwaInterfejsu i pole NazwaInterfejsu )

0

W ten sposób który mówisz mam teraz napisane, że jest to po prostu klasa ogólna (abstrakcyjna) Silnik. Tylko robiąc w ten sposób nie mam dostępu do pól i metod np silnika BenzynyTurbo (np. związanych z turbosprężarką, której nie ma w klasie abstrakcyjnej ponieważ nie każdy silnik ją posiada). Co do interfejsu to też nie jestem pewien ponieważ im dalej rozszerzane są klasy tym (w moim przypadku) więcej rzeczy dochodzi więc implementując go raczej nie zda egzaminu, chyba że się mylę.

0

Przy zadawaniu tego rodzaju pytań radziłbym, abyś wklejał kod. Nawet nieprawidłowy kod, który się nie buduje, ale przedstawia, co próbujesz osiągnąć, przybliżyłby nam problem dużo lepiej, niż słowny opis.

W tej chwili nie jestem pewien, czy w ogóle dobrze rozumiem, o co ci chodzi. Jeśli tak, to mam wrażenie, że używasz dziedziczenia nieprawidłowo. Otóż na tym właśnie opiera się jego sens, że - w kontekście twojego przykładu - samochód nie musi się znać na różnych typach silników. Na przykład nie musi, a wręcz nie powinien wiedzieć, że silnik diesla trzeba obsługiwać w jakiś specyficzny sposób. O to chodzi, że silnik ma sam martwić się o siebie, a wiedza na temat jego specyfiki nie ma "przeciekać" gdzie indziej. Jeżeli próbujesz to przeskoczyć, to jest takie jedzenie zupy widelcem. Oczywiście ludzie w miarę inteligentni wymyślą dla ciebie kilka sprytnych sposobów na zjedzenie zupy widelcem, ale nie zmienia to faktu, że zabierasz się do rzeczy źle.

Z tym że - patrz wyżej. Show me your code

0

Skoro masz klasę Silnik, to jest ona niejako interfejsem do wszystkich silników. Ta klasa powinna mieć metody dla każdego silnika, czyli powiedzmy odpalMotor, przyspiesz, zgaś. I właściwie tyle. Kwestia jak to ma być zrobione jest implementowana w klasach specjalizowanych w ich metodach prywatnych. Z punktu widzenia samochodu nie jest ważne jak silnik to zrobi, tak więc i ty nie musisz się nad tym zastanawiać.

Inna sprawa jak wczytać właściwy silnik z pliku. Konstrukcję musisz mieć taką, że do pola typu Silnik przypisujesz specjalizowany obiekt. Aby go utworzyć po nazwie możesz użyc metody Class.forName. Nie mniej jednak tutaj to tak do celów edukacyjnych.

Jeszcze jedna rzecz, którą możesz zrobić, ale tego nie polecam, to rzutowanie na typ specjalizowany. W klasie Silnik robisz pole "typ", które ma wartość charakterystyczną dla klasy. Możesz z odczytać jakiego typu jest silnik i wtedy zrobić rzutowanie na klasę specjalizowaną. Nie jest to zbyt eleganckie rozwiązanie. Możesz też dla obiektu silnik odczytać jakiej jest klasy poprzez getClass. Nadal jest to mało eleganckie rozwiązanie.

0

Abstrakcja silnk jest właściwa dla abstrakcji samochód. Jeżeli chcesz by podklasa silnika miała dodatkowe API to może powinieneś utworzyć dodatkową podklasę samochód, która będzie reprezentowała konkretny model?

0

Ok. Nie wkleję wszystkiego bo trochę tego jest i niektóre rzeczy nic nie wniosą więc wkleję zarys tego jak to mniej więcej wygląda

abstract class Silnik{
	private String ukladCylindrow, nazwaSilnika;
	private int pojemnosc, moc, momentObrotowy, iloscCylindrow;
	
	private double rozrzadKompletny, sprzeglo, pasekOsprzetu, filtrPowietrza, filtrOleju, glowica, tloki, walKorbowy, panewki, chlodnica, ukladWydechowy;
	private double iloscOleju, iloscPlynuChlodniczego, jakoscOleju, jakoscPlynuChlodniczego;
	
	public Silnik(){}
	
	public Silnik( ... ){
		// iniciowanie pól
	}
	
	/**
	 * Ogólnie gettery i settery do pól
	 */
         
         .......
	
	/**
	 * Metody do obslugi wartosci pol
	 */

         ......

}


class SilnikBenzynowy extends Silnik{
	private final String rodzaj = "Benzynowy";

	private double swieceZaplonowe, cewkaZaplonowa, przewodyWN;
	
	public SilnikBenzynowy(  ...  )
	{	
		super( ... );
		// iniciowanie pól

	}
	
	/**
	 * Gettery i settery
	 */
	
	/**
	 * Metody do obslugi wartosci pol itp. 
	 */
	
}

class SilnikBenzynowyTurbo extends SilnikBenzynowy{
	private final String rodzaj = "Benzynowy Turbo";
	
	private double turbo;
	
	public SilnikBenzynowyTurbo( ... )
	{
		super( ... );
		// iniciowanie pola
	}
	
	/**
	 * gettery i settery
	 */
	
	/**
	 * Metody do operacji na wartości pól
	 */
	
}

class SilnikDiesla extends Silnik{
	private final String rodzaj = "Diesel";
	
	private double swieceZarowe;
	
	public SilnikDiesla( ... )
	{
		super( ...  );
		// iniciowanie pola
	}
	
	/**
	 * gettery i settery
	 */
	
	/**
	 * Metody do operacji na wartości pól
	 */
}

class SilnikTurboDiesel extends SilnikDiesla{
	private final String rodzaj = "Turbo Diesel";
	
	private double turbo, wtryskiwacze, filtrDPF;
	private boolean isDPF;
	
	public SilnikTurboDiesel( ... )
	{
		super( ... );
		// iniciowanie pól
	}
	
	/**
	 * Kolejny konstruktor, potrzebny był, implementacja raczej nie ważna :)
	 */
	public SilnikTurboDiesel( ... )
	{
		super( ... );
		// iniciowanie pól
	}

	/**
	 * gettery i settery
	 */
	
	/**
	 * Metody do operacji na wartości pól
	 */
}

abstract class Pojazd{
	private String rodzaj, marka, model;
	private int rokProdukcji, przebieg, cena;
	
	protected UbezpieczenieIBadanieTechniczne ubezpieczenieBadanieTechniczne;
	
	public Pojazd( ... ){
		// iniciowanie pól
		
		ubezpieczenieBadanieTechniczne = new UbezpieczenieIBadanieTechniczne();
	}
	
	/**
	 * gettery i settery
	 */
	
}

class SamochodBaza extends Pojazd{
	private String generacja, rozdzajNadwozia, naped;
	private double zbiornikPaliwa, iloscPaliwa, filtrPaliwa;
	
	protected Nadwozie nadwozie;
	protected Podwozie podwozie; 
	protected Silnik silnik;
	protected SkrzyniaBiegow skrzyniaBiegow;
	

        // Konstruktor dla każdego z silników z osobna, nie wiem  w zasadzie czy tak powinno być ale w ten sposób zrobiłem
	public SamochodBaza( ... )
	{
		super();
		// iniciowanie pól
		
		nadwozie = new NadwozieOsobowe( ... );
		podwozie = new Podwozie( ... );
		silnik = new SilnikBenzynowy( ... );
		skrzyniaBiegow = new SkrzyniaBiegow( ... );
	}
	
	public SamochodBaza( ... )
	{
		super( ... );
		// inicjowanie pól
		
		nadwozie = new NadwozieOsobowe( ... );
		podwozie = new Podwozie( ...  );
		silnik = new SilnikBenzynowyTurbo( ... );
		skrzyniaBiegow = new SkrzyniaBiegow( ...  );
	}
	
	public SamochodBaza( .... )
	{
		super( .... );
		//i inicjowanie pól 

		nadwozie = new NadwozieOsobowe( ... );
		podwozie = new Podwozie( ... );
		silnik = new SilnikDiesla( ... );
		skrzyniaBiegow = new SkrzyniaBiegow( ... );
	}
	
	public SamochodBaza( ....  )
	{
		super( ...  );
		// inicjowanie pól
		nadwozie = new NadwozieOsobowe( ...  );
		podwozie = new Podwozie( ... );
		silnik = new SilnikTurboDiesel( ...  );
		skrzyniaBiegow = new SkrzyniaBiegow( ... );
	}
	
	/**
	 * Gettery i Settery
	 */
	
}

0

W klasach dziedziczących po Silnik masz pole rodzaj, które raczej bym zamienił, aby był to typ ENUM. Jak na siłę kombinować, to spróbowałbym zagregować te specyficzne parametry silników do jakiegoś wspólnego pola, dostępnego z klasy nadrzędnej. Może do typu Map, gdzie kluczem byłaby nazwa parametru? Ale to tak na szybko, nie wiem co by wyszło.

0
chodnik napisał(a):

Skoro masz klasę Silnik, to jest ona niejako interfejsem do wszystkich silników. Ta klasa powinna mieć metody dla każdego silnika, czyli powiedzmy odpalMotor, przyspiesz, zgaś. I właściwie tyle. Kwestia jak to ma być zrobione jest implementowana w klasach specjalizowanych w ich metodach prywatnych. Z punktu widzenia samochodu nie jest ważne jak silnik to zrobi, tak więc i ty nie musisz się nad tym zastanawiać.

Inna sprawa jak wczytać właściwy silnik z pliku. Konstrukcję musisz mieć taką, że do pola typu Silnik przypisujesz specjalizowany obiekt. Aby go utworzyć po nazwie możesz użyc metody Class.forName. Nie mniej jednak tutaj to tak do celów edukacyjnych.

Jeszcze jedna rzecz, którą możesz zrobić, ale tego nie polecam, to rzutowanie na typ specjalizowany. W klasie Silnik robisz pole "typ", które ma wartość charakterystyczną dla klasy. Możesz z odczytać jakiego typu jest silnik i wtedy zrobić rzutowanie na klasę specjalizowaną. Nie jest to zbyt eleganckie rozwiązanie. Możesz też dla obiektu silnik odczytać jakiej jest klasy poprzez getClass. Nadal jest to mało eleganckie rozwiązanie.

Nie wiem czy ten kod który wkleiłem mówi wszystko ale ogólne metody do obsługi każdego z silników mam właśnie tak zrobione. Jedynie "specjalizowane" dla każdego z innych typów są w innych klasach. Jednak chciałbym żeby nieważne jaki typ wrzucić ogólnie do pojazdu mieć możliwość korzystania z metod i pól podklas. Tak jak mówiłem robiąc pole klasy abstrakcyjnej mam większość metod ale ogólnych. W przypadku gdy np będę miał dwa obiekty tej klasy lecz z dwoma innymi "silnikami" sposobem którym zrobiłem są one nierozróżnialne (pod kątem silnika) ponieważ udostępniają metody do obsługi ogólnej silnia a nie konkretnego typu.

Nie miałem możliwości wcześniej przyjrzeć się jak powinno się pisać kod który będzie pisany zgodnie ze sztuką stąd te błędy :)

0

Konstruktor dla każdego z silników z osobna, nie wiem  w zasadzie czy tak powinno być ale w ten sposób zrobiłem

Raczej nie powinno. Oczywiście twój przykład luźno ma się do prawdziwych zastosowań projektowania obiektowego (silnik i samochód to takie metafory). Niemniej typowe rozwiązanie jest takie, że silnik jest do samochodu "wstrzykiwany" z zewnątrz.

public class Car { // proponuję przestawić się na konwencję anglojęzyczną - to złoty standard kodowania
    public Car(Engine engine /* nie wiemy i nie obchodzi nas, jakiego typu jest ten silnik */ ) 
}

Fakt, że twój samochód zna rozmaite typy silników i on je tworzy, sam w sobie łamie wiele dobrych zasad.

Na przykład https://pl.wikipedia.org/wiki/Zasada_otwarte-zamkni%C4%99te jest naruszona, ponieważ przy twoim rozwiązaniu dodanie jakiegokolwiek nowego typu silnika wymusza nanoszenie odpowiednich zmian w klasie Car. Łamie to zarazem https://pl.wikipedia.org/wiki/Zasada_jednej_odpowiedzialno%C5%9Bci

Lektura obowiązkowa dla ciebie to zasady składające się na SOLID.

Pokaż proszę konkretny scenariusz, w którym potrzebne ci jest, by klasa Car miała dostęp do cechy specyficznej dla konkretnego typu silnika (np. filtrDPF). Wtedy będzie można zaproponować rozwiązanie alternatywne.

Kluczową ideą programowania obiektowego jest właśnie to, że kod zostaje rozcięty na obiekty, których wiedza o sobie nawzajem jest minimalna. A ty chcesz, żeby znały się z sobą jak łyse konie. Samochód ma znać się na całej genealogii silników z wszystkimi jej odgałęzieniami, a nawet rozmaitych ich szczegółach technicznych... to jest zaprzeczenie sensu tego podejścia.

A przy okazji, te powielone deklaracje to też lipa:

        nadwozie = new NadwozieOsobowe( ... );
        podwozie = new Podwozie( ...  );
        skrzyniaBiegow = new SkrzyniaBiegow( ...  );

Nawet pozostając przy twoim (kiepskim) wzorcu, nie ma powodu by identyczne kawałki kodu były przeklejone do każdego konstruktora osobno.

Te inicjalizacje można by przenieść albo na poziom pól (jeśli zawsze są takie same), czyli po prostu:

    private Nadwozie nadwozie = new NadwozieOsobowe(...);

Albo np. przenieść do prywatnej metody, która będzie wywoływana w poszczególnych konstruktorach.

0
V-2 napisał(a):
public class Car { // proponuję przestawić się na konwencję anglojęzyczną - to złoty standard kodowania
    public Car(Engine engine /* nie wiemy i nie obchodzi nas, jakiego typu jest ten silnik */ ) 
}

Myślałem zrobić tak jak mówisz ale nie wiem dokładnie jak to zrobić by Klasa Pojazd czy jak by to zwał nie miała pojęcia jaki rodzaj silnika się w niej znajduje. To byłoby bardzo dobre rozwiązanie ale niestety nie wiem jak to zrobić by w ten sposób robić to w konstruktorze. Czyli mając jedno pole do silnika i dodawać ogólnie silnik niezależnie od jego typu. W jaki sposób to w takim razie rozwiązać?

V-2 napisał(a):

Pokaż proszę konkretny scenariusz, w którym potrzebne ci jest, by klasa Car miała dostęp do cechy specyficznej dla konkretnego typu silnika (np. filtrDPF). Wtedy będzie można zaproponować rozwiązanie alternatywne.

Chciałem to zrobić w taki sposób, że mając powiedzmy jakiś już konkretny model samochodu czy też tablicę obiektów samochodów i np. do konkretnego już samochodu sprawdzić w jakim stanie jest ten filtr czy też, po zakupie części, regeneracji czy cokolwiek zwiększyć tę wartość.

	
	// ...
        
	SamochodBaza audi = new SamochodBaza( ... );
	System.out.println("Stan filtra: " + audi.silnik.getFiltrDPF()); // przykładowe użycie

Co do tego:

        nadwozie = new NadwozieOsobowe( ... );
        podwozie = new Podwozie( ...  );
        skrzyniaBiegow = new SkrzyniaBiegow( ...  );

To zamiar jest taki, że przy tworzeniu nowego samochodu, dane te są oddzielne, tzn. w innym "module" wczytywane są dane z pliku, którymi uzupełniane są dane o samochodzie, tam też wczytywane są dane o skrzyni itp. a niektóre dane, związane z konkretnymi częściami czy to skrzyni, czy to nadwozia są generowane losowo w innej metodzie. Gotowe już komponenty (skrzynia, nadwozie itp.) są przekazywane do konstruktora pojazdu i tam inicjowane. Też nie wiem czy to dobry styl projektowania ale tak to zrobiłem. Faktycznie tak jak mówisz lepiej byłoby zrobić by ten kawałek kodu był wykonywany w osobnej metodzie.

Tak jeszcze od tematu. Z tego co widzę sama nauka programowania nie idzie w parze z dobrym stylem programowania i "myśleniem" obiektowym (chociaż myślałem, że robię to ok, najważniejsze chyba jest to że teraz wiem że robię to źle i zdałem sobie z tego sprawę :) ). Także chciałbym was jako doświadczonych zapytać czym się wspomagać (oprócz samego klepania kodu, i nauki języka i frameworków) by pisać lepszy kod przede wszystkim zgodny ze sztuką i "bardziej profesjonalnymi" zastosowaniami, czyli tak jak się to robi i tak by w przyszłości przy ewentualnym szukaniu pracy czy coś w tym stylu nie odbiegać od innych :)

0
Mały Orzeł 123 napisał(a):
V-2 napisał(a):
public class Car { // proponuję przestawić się na konwencję anglojęzyczną - to złoty standard kodowania
    public Car(Engine engine /* nie wiemy i nie obchodzi nas, jakiego typu jest ten silnik */ ) 
}

Myślałem zrobić tak jak mówisz ale nie wiem dokładnie jak to zrobić by Klasa Pojazd czy jak by to zwał nie miała pojęcia jaki rodzaj silnika się w niej znajduje. To byłoby bardzo dobre rozwiązanie ale niestety nie wiem jak to zrobić by w ten sposób robić to w konstruktorze. Czyli mając jedno pole do silnika i dodawać ogólnie silnik niezależnie od jego typu. W jaki sposób to w takim razie rozwiązać?

Nie jestem pewien, czego "nie wiesz jak zrobić". Z treści pytania i próbek kodu wynika, że rozumiesz co to jest konstruktor, parametr itd. Czego zatem brakuje? Przekazujesz silnik jako parametr w konstruktorze i tyle.


    public Car(Engine engine) {
         this.engine = engine;    
    }

    // gdzieś w fabryce samochodów
    Car car = new Car(new DieselEngine());

Przy takim rozwiązaniu odpowiedzialność składania samochodu (wyboru i wpakowania do niego silnika) zostaje wyniesiona gdzieś na zewnątrz. Vide wzorzec projektowy "budowniczy".

V-2 napisał(a):

Pokaż proszę konkretny scenariusz, w którym potrzebne ci jest, by klasa Car miała dostęp do cechy specyficznej dla konkretnego typu silnika (np. filtrDPF). Wtedy będzie można zaproponować rozwiązanie alternatywne.

Chciałem to zrobić w taki sposób, że mając powiedzmy jakiś już konkretny model samochodu czy też tablicę obiektów samochodów i np. do konkretnego już samochodu sprawdzić w jakim stanie jest ten filtr czy też, po zakupie części, regeneracji czy cokolwiek zwiększyć tę wartość.

Nie wymaga to przecież, by samochód wiedział, jaki ma silnik. Wystarczy mu, że ma Engine. Do sprawdzenia np. stanu filtru silnika będziesz mieć inny, wyspecjalizowany obiekt, nazwijmy go Mechanik, który rozpozna rodzaj silnika w samochodzie i wykona odpowiednie operacje.

	
	// ...
        
	SamochodBaza audi = new SamochodBaza( ... );
	System.out.println("Stan filtra: " + audi.silnik.getFiltrDPF()); // przykładowe użycie

To jest łamanie prawa Demeter. Zob. np. http://adam.wroclaw.pl/2014/08/prawo-demeter-wyjasnione-po-ludzku/

To duże uproszczenie, ale to prawo Demeter jest skrótowo nazywane "prawem jednej kropki". Czyli jeśli w kodzie masz coś w rodzaju x.y.z(), to znaczy, że coś jest nie tak. Przeskakujemy strukturę zarządzania - dyrektor idzie bezpośrednio do magazynu, żeby tam wydać jakieś polecenie. Inny przykład to wyprowadzanie psa. Mówimy psu, żeby szedł. Nie obchodzi nas, jak on to robi. Nie mówimy nogom psa, żeby szły. Pies ma kontrolować swoje nogi, a my psa.

Odnosząc się do przypadku, który podałeś, można by zrobić np. tak, że to silnik ma metodę np. getStatus(), która zwraca Stringa opisującego jego stan. Wtedy nie muszę wiedzieć, z jakim rodzajem silnika mam do czynienia. Dieslowy napisze, co tam u niego z filtrem, a parowy jakie ma ciśnienie w kotle : )

Żeby uniknąć owych wielu kropek, sam samochód możemy wyposażyć w metodę przekierowującą, np. getEngineStatus, która (pod spodem) wywoła this.engine.getStatus(). Dzięki temu, że nie przeskakujemy hierarchii po kilka stopni naraz, nasz kod pozostaje elastyczny.

Tak jeszcze od tematu. Z tego co widzę sama nauka programowania nie idzie w parze z dobrym stylem programowania i "myśleniem" obiektowym (chociaż myślałem, że robię to ok, najważniejsze chyba jest to że teraz wiem że robię to źle i zdałem sobie z tego sprawę :) ). Także chciałbym was jako doświadczonych zapytać czym się wspomagać (oprócz samego klepania kodu, i nauki języka i frameworków) by pisać lepszy kod przede wszystkim zgodny ze sztuką i "bardziej profesjonalnymi" zastosowaniami, czyli tak jak się to robi i tak by w przyszłości przy ewentualnym szukaniu pracy czy coś w tym stylu nie odbiegać od innych :)

Myślę, że warto by poczytać o teorii (dałem już kilka propozycji, z czym się zapoznać) plus robić to, co właśnie zrobiłeś, czyli wrzucać kod do oceny.

0
V-2 napisał(a):

    public Car(Engine engine) {
         this.engine = engine;    
    }

    // gdzieś w fabryce samochodów
    Car car = new Car(new DieselEngine());

Przy takim rozwiązaniu odpowiedzialność składania samochodu (wyboru i wpakowania do niego silnika) zostaje wyniesiona gdzieś na zewnątrz. Vide wzorzec projektowy "budowniczy".

Zrobiłem tak jak mówisz z jedną różnicą. Z tym że w konstruktorze mam przekazane nie tylko obiekty samego silnika ale też inne, związane z zawieszenie, skrzynią itp. Przekazuję już konkretny silnik, nieważne czy to Diesla czy Benzynowy. Dlatego też zrobiłem kilka konstruktorów zależnie od ich typu. Różnica jest właśnie taka że robię tak jak wcześniej w kodzie, czyli

	public Car(Engine engine){
		this.engine = new PetrolEngine( engine.getType, engine.getName. ... ); // tak to u mnie wygląda, też nie wiem czy dobrze w ten sposób
		// ...
	}
V-2 napisał(a):

Nie jestem pewien, czego "nie wiesz jak zrobić". Z treści pytania i próbek kodu wynika, że rozumiesz co to jest konstruktor, parametr itd. Czego zatem brakuje? Przekazujesz silnik jako parametr w konstruktorze i tyle.

Czego nie wiem? W zasadzie po napisaniu, że jeśli stosuję omijanie hierarchii to łamię zasadę to już takie coś nie jest mi potrzebne. Jednak dalej zastanawia mnie jedno. Jeśli pole w Car będzie typu Engine (jakiś tam podstawowy, abstrakcyjny) to on nie będzie mi udostępniał (chyba) metod podklas czyli już odpadnie mi metoda do obsługi turbo w silnikach trubo diesel czy turbo benz. Chyba, że się mylę.

Co do tego:

V-2 napisał(a):

Nie wymaga to przecież, by samochód wiedział, jaki ma silnik. Wystarczy mu, że ma Engine. Do sprawdzenia np. stanu filtru silnika będziesz mieć inny, wyspecjalizowany obiekt, nazwijmy go Mechanik, który rozpozna rodzaj silnika w samochodzie i wykona odpowiednie operacje.

	
	// ...
        
	SamochodBaza audi = new SamochodBaza( ... );
	System.out.println("Stan filtra: " + audi.silnik.getFiltrDPF()); // przykładowe użycie

Odnosząc się do przypadku, który podałeś, można by zrobić np. tak, że to silnik ma metodę np. getStatus(), która zwraca Stringa opisującego jego stan. Wtedy nie muszę wiedzieć, z jakim rodzajem silnika mam do czynienia. Dieslowy napisze, co tam u niego z filtrem, a parowy jakie ma ciśnienie w kotle : )

Żeby uniknąć owych wielu kropek, sam samochód możemy wyposażyć w metodę przekierowującą, np. getEngineStatus, która (pod spodem) wywoła this.engine.getStatus(). Dzięki temu, że nie przeskakujemy hierarchii po kilka stopni naraz, nasz kod pozostaje elastyczny.

To jeśli chciałbym mieć możliwość z Klasy Car dostęp do każdej z metod klasy jakiegokolwiek silnika muszę zrobić osobne metody do pobierania tych danych? Właśnie zrobiłem to celowo żeby nie pisać:

class Car{
	private Engine engine;
	// ....

	/*
		Konstruktor i inne metody
	*/

	public double getEngineStatusDPF(){
		return this.engine.getFiltrDPF();
	}

	// i tak dla każdej metody silnika, tym bardziej że chyba nie będę miał możliwości sprawdzenia danych specyficznych dla konkretnego typu silnika.
}
0
Mały Orzeł 123 napisał(a):

Zrobiłem tak jak mówisz z jedną różnicą. Z tym że w konstruktorze mam przekazane nie tylko obiekty samego silnika ale też inne, związane z zawieszenie, skrzynią itp. Przekazuję już konkretny silnik, nieważne czy to Diesla czy Benzynowy. Dlatego też zrobiłem kilka konstruktorów zależnie od ich typu. Różnica jest właśnie taka że robię tak jak wcześniej w kodzie, czyli

	public Car(Engine engine){
		this.engine = new PetrolEngine( engine.getType, engine.getName. ... ); // tak to u mnie wygląda, też nie wiem czy dobrze w ten sposób
		// ...
	}

"Kilka konstruktorów zależnie od ich typu" - czyli czym różnią się te konstruktory? No bo ten konstruktor, w którym tworzony jest PetrolEngine, ma sygnaturę Car(Engine engine). Jakie są sygnatury pozostałych konstruktorów?

Purysta powiedziałby, że ten fragment powyżej jest już sam w sobie bardzo podejrzany, bo tworzenie silników jest już pewną odpowiedzialnością. Czyli nad zasadą pojedynczej odpowiedzialności rośnie znak zapytania, skoro samochód pełni co najmniej jedną dodatkową odpowiedzialność - poza byciem samochodem, umie jeszcze budować silniki. Ja tam entuzjastą motoryzacji nie jestem, ale o zachowującym się w taki sposób samochodzie nie słyszałem.

To jeśli chciałbym mieć możliwość z Klasy Car dostęp do każdej z metod klasy jakiegokolwiek silnika muszę zrobić osobne metody do pobierania tych danych? Właśnie zrobiłem to celowo żeby nie pisać:

class Car{
	private Engine engine;
	// ....

	/*
		Konstruktor i inne metody
	*/

	public double getEngineStatusDPF(){
		return this.engine.getFiltrDPF();
	}

	// i tak dla każdej metody silnika, tym bardziej że chyba nie będę miał możliwości sprawdzenia danych specyficznych dla konkretnego typu silnika.
}

Dlatego też nie zasugerowałem stworzenia metody Car#getEngineStatusDPF, nieprawdaż? Podsunąłem całkiem inny pomysł - żeby silnik (każdy silnik) eksponował tylko ogólną metodę getStatus, która pozostanie najmniejszym wspólnym mianownikiem dla wszystkich typów silnika.

Wtedy odpowiedzialność za rozróżnianie rozmaitych typów silników jest przeniesiona do ich samych - bo każdy typ silnika sam dba o swoją implementację getStatus. Szczegóły tej implementacji są dla nas nieprzejrzyste, martwi się nimi tylko dany silnik, co jest słuszne, bo chcemy realizować zasadę "niech każdy zajmie się sobą i nie zawraca innym obiektom d**y".

Można tę odpowiedzialność przenieść sobie jeszcze gdzieś indziej, tu nie ma jednej prawidłowej odpowiedzi.

Istotne jest, że wg mnie ona nie powinna być umiejscowiona w klasie Car, bo prowadzi to do takich problemów, na które się natknąłeś, i tak naprawdę anuluje atuty programowania obiektowego. Wprowadza to bowiem silne powiązania między różnymi typami obiektów. "Przecieka" pomiędzy nimi bogata wiedza na temat siebie nawzajem, np. znają dobrze swoje rodziny. Zamykanie kodu w klasach nie polega na tym, że postawimy sobie po obu końcach klasy nawiasy klamerkowe, tylko właśnie na tym, że izolujemy tę szczegółową wiedzę przed światem zewnętrznym.

Mam wrażenie, że zaczynam się powtarzać

0
V-2 napisał(a):

"Kilka konstruktorów zależnie od ich typu" - czyli czym różnią się te konstruktory? No bo ten konstruktor, w którym tworzony jest PetrolEngine, ma sygnaturę Car(Engine engine). Jakie są sygnatury pozostałych konstruktorów?

Faktycznie źle trochę napisałem. Wyglądają one mniej więcej tak:

	Car(PetrolEngine engine, ... ){
		// ...
	}

	Car(TurboPetrolEngine engine, ... ){
		// ...
	}

	Car(DieselEngine engine, ... ){
		// ...
	}
	
	Car(TurboDieselEngine engine, ... ){
		// ...
	}

Czyli jak rozumiem takie podeście jest złe? Musiałbym stworzyć W klasie Car obiekt typu Engine jako ten najbardziej podstawowy, a potem w konstruktorze dać po prostu przypisanie? Dodatkowo dać sobie metodę która jest ogólną o wczytującą dane o silniku gdzie będą przekazane wszystkie dane, dla każdego silnika inne? A co jeśli chciałbym wczytać konkretną rzecz dotyczącą silnika w jakimś samochodzie?

0

Więc jak wczytać konkretną wartość?

1 użytkowników online, w tym zalogowanych: 0, gości: 1