Wzorce projektowe
Wzorce-kreacyjne
Wzorce-czynnościowe
Wzorce-strukturalne
Pozostałe
Robimy np. żeby nie robić konstruktorów z wieloma elementami.
Robi się klasę np. HouseBuilder (ctrl+i na konstruktorze i wpisać builder), najlepiej jako statyczną i wewnątrz klasy którą chcemy zrobić. Tam są wszystkie setery, które zwracają też HouseBuilder, żeby można było je robić jeden za drugim. Na końcu metoda build, która już stworzy oryginalną klasę i ma wszystko w seterze. Konstruktor oryginalnej klasy może być prywatny.
public class House {
private String address;
private Integer floorsNumber;
private House(String address, Integer floorsNumber) {
this.address = address;
this.floorsNumber = floorsNumber;
}
public static class HouseBuilder {
private String address;
private Integer floorsNumber = 1; //może być coś domyślnego
public HouseBuilder setAddress(String address) {
this.address = address;
return this;
}
public HouseBuilder setFloorsNumber(Integer floorsNumber) {
this.floorsNumber = floorsNumber;
return this;
}
public House build() {
return new House(address, floorsNumber);
}
}
}
Pozwala utworzyć obiekt za pomocą innego obiektu np. Boolean.valueOf(), Obiekt z jsona, albo jakieś dom z innego domu. Metody: from (na podstawie), of (z, np. json, xml), valueOf (jest wartością), instanceOf
FamilyHouse familyHouse = FamilyHouse.from(myHouse);
// i w klasie:
public static FamilyHouse from(House myHouse) {
return new FamilyHouse(myHouse.getAddress(), myHouse.getFloorsNumber());
}
Zalety statycznych metod wytwórczych:
- posiadają nazwę
- nie jest wymagane utworzenie obiektu
- mogą zwracać typ, który jest podtypem zdefiniowanego, zwracanego typu, i ten zwracany typ może zależeć od parametrów
Wady:
- nie mogą być dziedziczone i nie można takich metod odróżnić od innych metod statycznych
- from — metoda konwersji typu, która przyjmuje jeden parametr i zwraca obiekt, który posiada tę samą wartość co parametr:
Date d = Date.from(instant);
- of — metoda agregująca, która przyjmuje wiele parametrów i zwraca instancję odpowiedniego typu zawierającą wszystkie parametry:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf — nieco bardziej rozwlekła alternatywa dla from i of, np.:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance lub instanceOf — zwraca obiekt opisany przez parametr (jeśli istnieje), ale nie można powiedzieć, że ma tę samą wartość:
StackWalker luke = StackWalker.getInstance(options);
- create lub newInstance — podobna do instance lub getInstance, poza tym że gwarantuje, iż każda zwracana instancja jest inna od pozostałych:
Object newArray = Array.newInstance(classObject, arrayLen);
- getType — podobna do getInstance, ale używana, gdy metoda fabryczna znajduje się w osobnej klasie. Type wskazuje na typ obiektu zwracanego przez metodę fabryczną:
FileStore fs = Files.getFileStore(path);
- newType — podobna do newInstance, ale używana, gdy metoda fabryczna znajduje się w osobnej klasie. Type wskazuje na typ obiektu zwracanego przez metodę fabryczną:
BufferedReader br = Files.newBufferedReader(path);
- type — zwięzła alternatywa dla getType lub newType:
List<Complaint> litany = Collections.list(legacyLitany);
Jeden obiekt na 1 program, intensywnie używany w większości klas np. log, stan autentyfikacji, cache, repozytoriom, coś do śledzenia, coś co się nie zmienia bez względu na miejsce użycia. Trzeba uważać żeby był bezpieczny wątkowo.
public class Logger {
private static Logger instace;
private Logger() {
}
public static Logger getInstace() {
return SingletonHolder.INSTANCE;
}
public void logToConsole(){
}
private static class SingletonHolder {
private static final Logger INSTANCE = new Logger();
}
}
Można też jako 1 - elementowy enum:
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Żeby dzielić duże klasy na mniejsze z 1 odpowiedzialnością i nimi zarządzać.
Jest obiekt obserwujący i obserwowany. Obserwowany powiadamia obserwującego(cych) o zmianach. Np. obsługa interfejsu użytkownika (jakaś akcja na kliknięciu), reakcja na coś (np. zmiany repozytorium), obsługa zmiany w czujnikach. Klasa Observer jest przestarzała, bo nie można jej serializować, nie jest bezpieczna wątkowo, i nie dostarcza zbyt dużo informacji You can use PropertyChangeEvent and PropertyChangeListener from java.beans package. Można też samemu to zrobić. Czyli w jednej klasie trzymamy tablicę gdzie będą obserwatory - klasy implementujacy jakiś interfejs z 1 funkcją, która na nich będzie wywoływana. Są tam też metody do dodawania, usuwania i powiadomienia obserwatorów. I w zasadzie tyle. Tu jest przykład taki ze streemam https://community.oracle.com/docs/DOC-1006738
Robimy interfejs(y) np. JobStrategy i jego implementacje np. DoctorJobStrategy. Potem jakiś obiekt będzie miał zmienną(e) typu tego(tych) interfejsu, ustawianą gdzieś z zewnątrz. Następnie obiekt ma metodę, w której jest wykonanie akcji na na tamtej zmiennej. Czyli można dynamicznie takie strategie ustawiać obiektowi i je zmieniać w razie potrzeby.
public interface JobStrategy {
void doYourJob();
}
public class DoctorJobStrategy implements JobStrategy {
@Override
public void doYourJob() {
System.out.println("Work as doctor");
}
}
public class Employee implements TravelStrategy, JobStrategy {
public JobStrategy jobStrategy;
@Override
public void doYourJob() {
jobStrategy.doYourJob();
}
}
public static void main(String[] args) {
Employee mike = new Employee();
mike.jobStrategy = new DoctorJobStrategy();
mike.doYourJob();
}
Jej zadaniem jest zdefiniowanie metody, będącej szkieletem algorytmu. Algorytm ten może być następnie dokładnie definiowany w klasach pochodnych. Niezmienna część algorytmu zostaje opisana w metodzie szablonowej, której klient nie może nadpisać (final). W metodzie szablonowej wywoływane są inne metody, reprezentujące zmienne kroki algorytmu. Mogą one być abstrakcyjne lub definiować domyślne zachowania. Klient, który chce skorzystać z algorytmu, może wykorzystać domyślną implementację bądź może utworzyć klasę pochodną i nadpisać metody opisujące zmienne fragmenty algorytmu.
Plusy: łatwo wprowadza się alternatywne implementacje, redukcja duplikacji kodu. Minusy: może trudna w utrzymaniu i sporo ogranicz kilentów - bo muszą się trzymać zdefiniowanych kroków
public abstract class AbstractExporter {
private final Report report;
private final PrintStream out;
AbstractExporter(Report report, PrintStream out) {
this.report = report;
this.out = out;
}
public final void export() { //stała metoda dla wszystkich klas pochodnych
beforeLabels(out);
handleLabels(out, report.getLabels());
afterLabels(out);
beforeRecords(out);
handleRecords();
afterRecords(out);
}
protected void beforeLabels(PrintStream out) { // metoda jak ta można nadpisać
}
...
Wzorce modelujące związki pomiędzy klasami, żeby były luźno powiązane. Łączenie, integracja różnych klas.
Rozszerza funkcjonalność na podstawie innego obiektu. Obiektu starego nie zmieniamy, a nowy adoptowujemy do nowych wymagań. Można np. połączyć nim różne api, ale raczej unikać żeby nie było za dużego zamieszania.
Robi się nową klasę, która albo rozszerza poprzednią albo lepiej - ma jako pole tą poprzednią i na niej wywołuje się stare lub nowe funkcjonalności
OffitialTrippingEmployee ofMike = new OffitialTrippingEmployee(mike); //rozszerzeamy fukcjonalność mika nie zmieniająć jego klasy
ofMike.goToClient();
-------------------
public void goToClient() {
employee.travelToWork(employee);
System.out.println("Travel to Klient"); // i tu coś nowego
}
Dekorujemy obiekt dodając mu coś nowego. Robi się to tak, że tworzy się taki łańcuch opakowując klasę inną klasą, a to można dalej opakowywać.
new FreqBonus(new DeadLinieBonus(new SpecialBonus(mike))).getSalary()
Każda klasa opakowywyjąca dziedziczy po klasie abstrakcyjnej. W której jest zmienna, która będzie zawierać to co do tej pory się nazabierało i konstruktor wpisujący to w tą zmienną.
public abstract class Bonus implements Payable{
private Payable payable;
Bonus(Payable payable){
this.payable = payable;
}
public int getSalary() {
return payable.getSalary() + getPaidBonus(payable.getSalary());
}
abstract protected int getPaidBonus(int salary);
}
-----------------------------------------
public class SpecialBonus extends Bonus {
public SpecialBonus(Payable payable) {
super(payable);
}
@Override
protected int getPaidBonus(int salary) {
return 1000;
}
}
Upraszcza metody które wystawiane dalej, żeby były prostsze, idiotoodporne i żeby nikt nie integrował w wewnętrzny system. Można robić kilka poziomów. Dobrze utrzymywać ten sam poziom w fasadach.
public class ApiFacade {
private EmployeeCreator employeeCreator = new EmployeeCreator(); //to taka fabryka
private EmployeeDatabase eDataBase = new EmployeeDatabase();
public Employee createDoctor(int i) {
Employee mike = employeeCreator.create(EmployeeCreator.JAKAS_STALA);
eDatabase.addEmployee(mike);
return mike;
}
public int countSalary(Employee mike){
//a tu coś jeszcze innego
}
}