Zarządzanie tożsamością i dostępem użytkownika, czyli o co chodzi z IDP?
Czym jest tożsamość użytkownika? Z czego wynika potrzeba zarządzania dostępem w firmie? Jak działa tzw. IDP? Odpowiedź na te pytania znajdziesz w artykule.
Hej, dzisiaj jako Innokrea opowiemy Wam o tym czym są wzorce projektowe i dlaczego każdy programista powinien mieć je w swojej wirtualnej skrzynce deweloperskich narzędzi. Jeśli jesteście zainteresowani, zapraszamy do lektury, a jeśli nie mieliście jeszcze okazji zapoznać się z naszymi wcześniejszymi artykułami dotyczącymi zasad SOLID, gorąco zachęcamy, ponieważ są one ściśle powiązane z tematem wzorców projektowych.
Wzorce projektowe to sprawdzone rozwiązania często występujących w programowaniu problemów. Dotyczą one przede wszystkim tego jakie klasy deweloper powinien stworzyć w celu rozwiązania danego problemu (inaczej, jak rozdzielić odpowiedzialność pomiędzy komponenty) oraz jak je połączyć ze sobą. Dzięki zastosowaniu wzorców sprawiamy, że nasz kod jest zgodny z zasadami SOLID i łatwo go rozszerzać o dodatkowe funkcjonalności. Wzorce zachęcają dewelopera do pisania zgodnie z zasadami SOLID. Bez wzorców pisanie dużych systemów jest trudniejsze ze względu na rosnącą złożoność zależności między komponentami. W praktyce oznacza to, że pojedyncza zmiana w kodzie może wpływać na komponenty, na które nie powinna. Rozwijanie takiego kodu stają się bardzo trudne, czasochłonne i przede wszystkim kosztowne. Zwiększamy wtedy ryzyko wprowadzania błędów i dług technologiczny.
Wzorce projektowe nie tylko upraszczają rozwój oprogramowania, ale także usprawniają współpracę między programistami. Jeżeli każdy członek zespołu zna wzorce, które można zastosować do rozwiązywania często występujących problemów, komunikacja w zespole i praca nad projektem stają się bardziej efektywne.
Istnieją dziesiątki wzorców projektowych adresujących różne problemy programistyczne, ale podstawowo wyróżniamy trzy kategorie:
Istnieją także inne rodzaje wzorców projektowych takie jak: dotyczące architektury czy programowania wielowątkowego.
Spróbujmy zaimplementować jeden ze wzorców behawioralnych – strategię. Pozwala on na zdefiniowanie rodziny algorytmów/strategii, ich enkapsulację i wymienność w czasie działania programu. Wzorzec ten zapewnia elastyczność, umożliwiając klientowi wybór bez zmiany kodu. Przykład, który przytoczymy na potrzeby tego artykułu dotyczy mechanizmów płatności sklepu internetowego. Wyobraźmy sobie, że nasz program jest aplikacją sklepu online, która ma za zadanie między innymi przetwarzać płatności użytkowników. Musimy jednak dopuszczać możliwość skorzystania z wielu mechanizmów płatności, a nasze rozwiązanie powinno być rozszerzalne o nowych dostawców płatności bez znacznej modyfikacji kodu. Na ratunek przychodzi wzorzec projektowy strategii.
Rysunek 1 – Diagram klas dla aplikacji sklepu internetowego
Nasza aplikacja ma kilka klas odpowiadających za symulowanie działania sklepu. Nie jest to oczywiście działanie kompletne i realne, ale mające za zadanie pokazać pewien kontekst aplikacji wzorca strategii. Widzimy klasę Store, która przechowuje obiekt IPaymentStrategy, będący interfejsem z metodą pay. Dzięki zastosowaniu interfejsu, klasy PaypalPayment i StripePayment mogą być użyte w miejscu IPaymentStrategy. Klient chcący dokonać płatności wybiera metodę płatności, a kod w klasie Store dzięki metodom setPaymentStrategy oraz executePayment jest w stanie zapłacić z użyciem dowolnego zaimplementowanego sposobu. Dzięki temu możemy łatwo zmieniać sposób płatności poprzez zastosowanie abstrakcji. Klasa Store nie zależy bezpośrednio od żadnej z implementacji, a jedynie od interfejsu.
Rysunek 2 – Diagram sekwencji dla zaimplementowanego przypadku użycia wzorca strategii
Kod w Javie implementujący powyższy przypadek przedstawiamy poniżej.
public interface IPaymentStrategy {
void pay(Double amount, String toAccountId);
}
public class PaypalPayment implements IPaymentStrategy{
@Override
public void pay(Double amount, String toAccountId) {
// Payment logic implementation
System.out.println("Paypal payment: "+amount+" paid to "+toAccountId);
}
}
public class StripePayment implements IPaymentStrategy{
@Override
public void pay(Double amount, String toAccountId) {
// Payment logic implementation
System.out.println("Stripe payment: "+amount+" paid to "+toAccountId);
}
}
public class Store {
// Online store class
Double cartValueAmount = 30.0;
String shopAccountId = "123321";
IPaymentStrategy paymentStrategy;
public Store(IPaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void setPaymentStrategy(IPaymentStrategy paymentStrategy){
this.paymentStrategy = paymentStrategy;
}
public void executePayment() {
System.out.println("Executing payment strategy with class: " + paymentStrategy.getClass().getName()); this.paymentStrategy.pay(this.cartValueAmount,this.shopAccountId);
System.out.println("Payment successful!\n");
}
}
public class Client {
public static void main(String[] args) {
System.out.print("Welcome to the shop! \n \n");
IPaymentStrategy paymentStrategy1 = new PaypalPayment();
IPaymentStrategy paymentStrategy2 = new StripePayment();
Store onlineStore = new Store(paymentStrategy1);
onlineStore.executePayment();
onlineStore.setPaymentStrategy(paymentStrategy2);
onlineStore.executePayment();
}
}
Drugim wzorcem behawioralnym, który zaimplementujemy, jest metoda szablonowa (Template Method), służąca do definiowania szkieletu algorytmu w klasie bazowej, przy jednoczesnym umożliwieniu podklasom nadpisywania poszczególnych kroków bez modyfikowania struktury algorytmu. Kontynuujmy przykład sklepu internetowego – tym razem w kontekście realizacji zamówienia, gdzie proces może wyglądać następująco: wybierz produkt, sprawdź, czy to prezent, zapakuj, jeśli tak, i dostarcz.
Rysunek 3 – wzorzec template method w kontekście sklepu
Wyobraźmy sobie sytuację w której sklep ma za zadanie przetwarzać zamówienia w określony sposób, który może wyglądać następująco: wybierz produkt, sprawdź czy jest oznaczony jako prezent, zawiń w papier prezentowy jeśli jest i dostarcz. Należy zauważyć, że taki proces jest mocno abstrakcyjny i jego konkretna implementacja może być nieco różna w zależności od kontekstu. W tym celu zastosujemy wzorzec template method, który pozwoli na zdefiniowanie abstrakcyjnego algorytmu, a następnie zastosowanie jego bardzo konkretnej implementacji. Zrobimy to przy użyciu klasy abstrakcyjnej OrderProcessTemplate i jej dwóch implementacji OnlineOrderProcess i StoreOrderProcess, które będą określały sposoby przetwarzania zamówienia online oraz do odbioru stacjonarnego.
Rysunek 4 – diagram sekwencji dla implementacji wzorca template method w powyższym przykładzie
Widzimy jak wywołanie metody processOrder powoduje wywołanie konkretnych implementacji danych metod w podklasach dziedziczących tj. OnlineOrderProcess i StoreOrderProcess. Dzięki temu można tworzyć kolejne ‘processory’ dla innych przypadków bez modyfikacji innych klas. Spójrzmy na kod implementujący te koncepcje w języku Java.
public class StoreOrderProcess extends OrderProcessTemplate {
public StoreOrderProcess() {
super();
}
@Override
protected void selectProduct() {
System.out.println("Selecting product from the inventory.");
}
@Override
protected void deliver() {
System.out.println("Customer will pick up the product from the store.");
}
}
public abstract class OrderProcessTemplate {
public OrderProcessTemplate() {
}
public final void processOrder() {
selectProduct();
if (isGift()) {
wrapGift();
}
deliver();
}
protected abstract void selectProduct();
protected abstract void deliver();
protected boolean isGift() {
return false;
}
public class OnlineOrderProcess extends OrderProcessTemplate {
public OnlineOrderProcess() {
super();
}
@Override
protected void selectProduct() {
System.out.println("Selecting product from online catalog.");
}
@Override
protected void deliver() {
System.out.println("Delivering product to the customer's address.");
}
@Override
protected boolean isGift() {
return true;
}
}
private void wrapGift() {
System.out.println("Gift wrapping completed.");
}
}
public class StoreOrderProcess extends OrderProcessTemplate {
public StoreOrderProcess() {
super();
}
@Override
protected void selectProduct() {
System.out.println("Selecting product from the inventory.");
}
@Override
protected void deliver() {
System.out.println("Customer will pick up the product from the store.");
}
}
Zwróćmy uwagę na rolę klasy abstrakcyjnej, która posiada implementacje metod processOrder oraz isGift, a z drugiej strony dwie metody abstrakcyjne selectProduct i deliver, które muszą zostać nadpisane w klasie dziedziczącej.
Gdyby połączyć razem dwa poprzednie przypadki, tak aby pokazać zastosowanie obu wzorców jednocześnie mogłoby to wyglądać jak na poniższym diagramie.
Rysunek 5 – Diagram klas dla połączonych wzorców Strategy i Template Method
Rysunek 6 – diagram sekwencji dla przypadku łączonego
Jeśli jesteście ciekawi jak wygląda kod ostatniego z rozwiązań to dołączamy całe repozytorium w załączniku.
Dzisiaj udało nam się opowiedzieć Wam czym są wzorce projektowe, jaką rolę spełniają i dlaczego warto je stosować. Przedstawiliśmy również kilka implementacji dzięki którym sami możecie zgłębić wiedzę w tym temacie. Jeśli jesteście ciekawi, to zajrzyjcie do kodu lub do linków w źródłach zawierających więcej informacji. Do usłyszenia w następnym wpisie!
Wszystkie powyższe przypadki są mocno dydaktyczne i nie przedstawiają w pełni aplikacji takich wzorców, ale pozwalają na zrozumienie tego jak działają i w jaki sposób powinny być stosowane.
Zarządzanie tożsamością i dostępem użytkownika, czyli o co chodzi z IDP?
Czym jest tożsamość użytkownika? Z czego wynika potrzeba zarządzania dostępem w firmie? Jak działa tzw. IDP? Odpowiedź na te pytania znajdziesz w artykule.
Bezpieczeństwo
Hej, hej... Programisto, to kolejny artykuł dla Ciebie! Druga część artykułu na temat wzorców projektowych. Poznaj Adapter oraz Memento.
Programowanie
Blockchain – płatności w świecie kryptowalut
Blockchainie - poznaj świat transakcji, kryptowalut i elektronicznych płatności.
BezpieczeństwoFinanse