Czy to jest Adapter?

1

LowRider to Adaptee?
PoliceCar to AdapterImplementation?
PrivilegedCar to Adapter?



package Adapter;

public interface PrivilegedCar {

	public void moveWithSiren();
	
	public void moveWithoutSiren();
}


package Adapter;

public class PoliceCar implements PrivilegedCar{
	
	private LowRider car;
	
	public PoliceCar(){
		this.car = new LowRider();
	}
	
	public void moveWithSiren(){
		System.out.println("*biu biu!* GETOUTA ROAD! *biu biu!* ");
	}
	
	public void moveWithoutSiren(){
		car.moveSlowlyInTheHood();
	}
	
	

}


package Adapter;

public class LowRider {

	public void moveSlowlyInTheHood(){
		System.out.println("havin a cool ride, dawg...");
	}
	
	public void runAwayFromPolice(){
		System.out.println("shieeeet... the cops! run!");
	}
}




package Adapter;

public class AppMain {
	public static void main(String[] args) {

		PoliceCar pc = new PoliceCar();
		pc.moveWithoutSiren();
		pc.moveWithSiren();
	
}

Jeśli tak to czy nie lepiej zrobić tak:



package Adapter;

public interface CityVehicle {

	default public void moveSlowlyInTheHood(){
		System.out.println("havin a cool ride, dawg...");
	}
}


package Adapter;

public interface PrivilegedCar {

	public void moveWithSiren();
	
	public void moveWithoutSiren();
}


package Adapter;

public class PoliceCar implements PrivilegedCar, CityVehicle{
	
	
	
	public PoliceCar(){
		
	}
	
	public void moveWithSiren(){
		System.out.println("*biu biu!* GETOUTA ROAD! *biu biu!* ");
	}
	
	public void moveWithoutSiren(){
		moveSlowlyInTheHood();
	}
	
	

}


package Adapter;

public class LowRider implements CityVehicle{

	
	
	public void runAwayFromPolice(){
		System.out.println("shieeeet... the cops! run!");
	}
}


package Adapter;

public class AppMain {
	public static void main(String[] args) {

		PoliceCar pc = new PoliceCar();
		pc.moveWithoutSiren();
		pc.moveWithSiren();
		
		
	}
}
2

imo to nie ma to zadnego sensu. co chcesz osiagnac?

4
katelx napisał(a):

imo to nie ma to zadnego sensu. co chcesz osiagnac?

Jak to co, on chce zastosować wzorce ;)

0

chcę osiągnąć implementację adaptera. :) Nie rozumiem po co robić adapter jak można postawić interfejs z domyślnymi metodami.

Tu chodzi o to, że LowRider to auto gangu i mają metody, które nie może mieć wóz policyjny PoliceCar poza 1 metodą.
Tę 1 metodę do wożenia się po dzielnicy chce mieć też wóz policyjny.

0

Załóżmy że masz biblioteke która pozwala holować samochody.

Twoje implementacje police car i low rider nie pozwalają na bycie holowanymi

Adapterem będzie więc klasa class TowingAdapter { .ctor(Vehicle vehicle ){} void AttachToTow(Tow tow) }

0

Od pewnego poziomu abstrakcji tłumaczenie obiektówki na zwierzątkach, czy przedmiotach codziennego użytku czy samochodach przestaje mieć sens (no chyba, że robisz grę albo coś innego, gdzie faktycznie występują zwierzęta, lodówki czy samochody jako obiekty dziedziny).

Lepiej wziąć to na przykładzie czegoś, co naprawdę ci się przyda. Np. chcesz wyświetlać komunikaty na stronie internetowej, ale nie wiesz jeszcze jak (czy może okienko alert, czy może wyświetlić to gdzieś w środku strony za pomocą jQuery, czy może komunikaty wyświetlać tylko w konsoli debuggerskiej itp.).

Ponieważ często będziesz wyświetlał komunikaty, potencjalna zmiana decyzji może cię kosztować zamianę np. 100 wywołań funkcji alert na adekwatne 100 wywołań funkcji console.log. Jest to bez sensu, więc potrzebne ci jest coś, co pozwala bardzo łatwo się przełączać pomiędzy róznymi implementacjami i nie uzależnia cię od detali technicznych. Więc możesz zrobić sobie 3 adaptery jQueryPrinter, alertPrinter i consolePrinter i każdy będzie miał taką samą metodę print:
(kod w JavaScript)


class jQueryPrinter {
   print(text) {
      $("#whatever").text(text); // wyswietla text za pomoca jQuery
   }
}

class AlertPrinter {
   print(text) {
      alert(text); // wyswietla okienko modalne z tekstem `text`
   }
}

class ConsolePrinter {
   print(text) {
      console.log(text); // wyswietla text w konsoli developerskiej
   }
}

a potem możesz operować na wyższym poziomie abstrakcji

const printer = new ConsolePrinter; // albo new AlertPrinter etc.

printer.print(2 + 2);
printer.print(1000 + 200 + 30 + 4);
printer.print('Hello World');

:)

2

@LukeJL: to co tu pokazałeś, to jest strategia, a nie adapter.

Adapter to taki kabelek, który z jednej strony ma HDMI, a z drugiej DVI. W programowaniu działa to dokładnie tak samo - pozwala połączyć jakiś obiekt X (stary monitor z DVI) z metodą, która przyjmuje obiekty określonego interfejsu I (czyli HDMI). Musisz w tym celu ten swój obiekt X opakować w obiekt Adapter, który będzie także implementował interfejs I (czyli kabelek konwertujący DVI na HDMI). Metody wymagane przez I w implementacji Adaptera będą w praktyce wołały metody obiektu X.

Ogólnie polecam przeczytać Head first design patterns.

2

To co chcesz osiągnąć to jakaś forma substytucji jednego typu innym i zadziałało by to w językach z tzw. kaczym typowaniem (duck typing - jeżeli coś kwacze jak kaczka i pływa jak kaczka to jest to kaczka, choć w rzeczywistości może to być coś zupełnie innego). Wzorzec adaptera służy do tłumaczenia, adaptacji, jednego interfejsu na inny. W twoim przypadku mogło by to wyglądać tak:

class LowRiderToPoliceCarAdapter implements PrivilegedCar{

	private LowRider lowRider;

	public LowRiderToPoliceCarAdapter(LowRider lowRider){
		this.lowRider = lowRider;
	}

	public void moveWithSiren(){
        	// pytanie jak lowRIder ma udawać wóz policyjny?
    	}
 
    	public void moveWithoutSiren(){
        	lowRider.moveSlowlyInTheHood();
    	}
}

I teraz masz coś co pozwala na adaptację LowRidera na samochód policyjny.

0

Jak czytam teraz definicje adaptera i strategii z książki GoF tak na szybko to wydaje mi się, że różnica jest głównie w przeznaczeniu "do czego ta klasa może służyć" * a niekoniecznie w implementacji - w obydwu wzorcach mamy rzecz A, która chce używać jakichś niekompatybilnych interfejsów X oraz Y w ten sam sposób, więc korzysta z jakiegoś wrappera, który udostępnia jednolity interfejs B.

Ja nie widzę różnicy, chociaż możliwe, że jest.

** Z tego, co wyczytałem i jak to rozumiem, to przy adapterze zwykle mamy już gotowy jakiś interfejs, którego używamy i tylko jeden z obiektów jest “czarną owcą” i chcemy go przyrównać do pionu (czyli trochę jak UK a Europa - gniazdka inne, mile, ruch lewostronny itp.). Natomiast przy strategii startujemy od zera i żaden obiekt nie zachowuje się standardowo (czyli trochę jak XKCD 927: https://xkcd.com/927/ ).
Przynajmniej tak to rozumiem.
*

0

@LukeJL: w adapterze masz dwie rzeczy - kod operujący na obiektach implementujących dany interfejs oraz obiekt, który tego interfejsu nie implementuje. Dlatego piszesz adapter, który opakowuje ten obiekt do istniejącego kodu.
W strategii masz zestaw różnych algorytmów o wspólnym interfejsie. Ok, możesz napisać strategię, która będzie przy okazji adapterem (bo opakuje jakąś istniejącą bibliotekę, aby realizowała cele strategii), ale strategia nadal będzie jakby poziom wyżej w abstrakcji.

0

@somekind: jeśli mam kolejkę albo stos na przykład to implementacja z lista pod spodem będzie adapterem?

2
Julian_ napisał(a):

chcę osiągnąć implementację adaptera. :) Nie rozumiem po co robić adapter jak można postawić interfejs z domyślnymi metodami.

zle dobrales przyklad, wez lepiej cos gdzie adapter jest faktycznie potrzebny. np:

zalozmy ze masz nowe api do obliczania cen czegostam:

interface PriceCalculator {
    Price getPrice(Instrument instr, FxConverter converter);
}

interface FxConverter {
    Price toUSD(Price price);
}

niestety nie masz za bardzo czasu zeby napisac nowy komponent do wymiany walut, za to masz dzialajace i przetestowane to:

class LegacyFxConverter {
    long toUSD(String ccy, long price) {
        /* tu duzo skomplikowanej logiki etc */
    }
}

potrzebujesz wiec adaptera:

class FxConverterAdapter implements FxConverter {
    LegacyFxConverter legacyConverter;

    public FxConverterAdapter(LegacyFxConverter legacyConverter) {
        this.legacyConverter = legacyConverter;
    }

    Price toUSD(Price price) {
       long usdPrice = legacyConverter.toUSD(price.getCcy(), price.getValue());
       return new Price("USD", usdPrice); 
    }
}
0

a ten interfejs PriceCalculator to po co? No, tak samo zrobiłem.

Pytanie po co tak robić skoro można przenieść implementację z LegacyFxConveter do interfejsu FxConveter jako domyślną metodę:

interface FxConverter {
    default public Price toUSD(String ccy, long price){
        /* tu duzo skomplikowanej logiki etc */
    }
}


class LegacyFxConverter implements FxConveter{
    
}


class FxConverterAdapter implements FxConverter {
    
}
0

@Julian_: no to teraz wyobraź sobie że to twoje "dużo skomplikowanej logiki" to jest jakiś cały moduł, cała biblioteka, moze setki klas :D I często nie da sie ot tak tej logiki przenieść kopiując jedną metodę. Zresztą duplikowanie kodu to tragiczny pomysł, bo co jak wyjdzie nowa wersja tej biblioteki? Znowu kopiujesz ten kod? No i będziesz nagle miał w projekcie ten sam kod kilka razy, co oznacza że będą błędy i niespójności.

0

Kopiuj wklej to potężna technika XD
żeby była możliwość zrobienia sprytnego kopiuj wklej na zasadzie referencji, czyli że przeklejasz kod w 100 miejsc i w tych 100 miejscach zawsze będzie najbardziej aktualna wersja... oh, wait. Przecież do tego właśnie można wykorzystać klasy XD

nawiasem mówiąc w JS da się pracować z kodem na poziomie samych metod (bo funkcje są obiektami), więc wystarczyłoby po prostu wydzielić gdzieś jedną funkcję do osobnego modułu i ją używać, a nie bawić się w dziedziczenie.

0
Julian_ napisał(a):

a ten interfejs PriceCalculator to po co?

jak to po co? przeciez napisalam ze to jest nowe api do ktorego chcesz sie dostosowac wiec potrzebujesz adapter bo masz istniejacy kod legacy ktory "nie pasuje" do nowych interfejsow.

Pytanie po co tak robić skoro można przenieść implementację z LegacyFxConveter do interfejsu FxConveter jako domyślną metodę

i trzymac caly modul do konwersji walut w interfejsie? zmieniac wszystkie dotychczasowe zaleznosci zeby uzywaly nowego sposobu? jesli twoj caly projekt to przyklad na uzycie adaptera to rzeczywiscie, nie ma to sensu. dlatego wlasnie sugeruje analizowac cos co rozwiazuje rzeczywisty problem a nie autka czy zwierzatka ktore wypisuja tekst na konsole ;)

0

Dobra @Julian_ w sumie znazłem w Javie dobry przykład na adaptera, tylko "lekko" schowany: private static class ArrayList<E> z Arrays.java która jest zwracana przez Arrays.asList(T... a) :)
Dzięki temu gdy masz tablicę obiektów możesz ją przekazać jako Listę, bo czasami jest tak że niektóre metody zwróca tablice np.
https://docs.oracle.com/javase/8/docs/api/java/io/File.html#listFiles--
A gdziesz będziesz chciał przekazac Liste bo jakas inna biblioteka potrzebuje jako parametru kolekcji plików a nie tablicy :)

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