Opowiedz o wzorcu projektowym, którego używałeś w swoim rozwoju.
sobes.tech AI
Odpowiedź od AI
Używałem następujących wzorców projektowych:
-
MVC (Model-View-Controller): Podział logiki aplikacji. Model zarządza danymi, Widok odpowiada za wyświetlanie, a Kontroler obsługuje interakcję użytkownika i aktualizuje Model lub Widok. Zastosowane w Angular, gdzie komponenty częściowo implementują tę ideologię.
-
MVVM (Model-View-ViewModel): Podobny do MVC, ale wprowadza ViewModel jako pośrednika między Widokiem a Modelem. ViewModel zawiera logikę biznesową do wyświetlania danych i obsługi działań użytkownika. Aktywnie używany z React i Vuex (Vue), gdzie stan aplikacji jest zarządzany centralnie.
// Przykład reaktywności w React z użyciem useState import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); // useState część mechanizmu MVVM const increment = () => { setCount(count + 1); // Aktualizacja stanu przez setter }; return ( <div> <p>Liczba: {count}</p> {/* Wyświetlanie stanu */} <button onClick={increment}>Zwiększ</button> {/* Działanie wpływające na stan */} </div> ); } -
Wzorzec komponentu: Podział interfejsu użytkownika na niezależne, wielokrotnego użytku komponenty. Każdy komponent enkapsuluje własną logikę, markup i style. Podstawa nowoczesnych frameworków, takich jak React, Vue i Angular.
// Przykład prostego komponentu React function Button(props) { return ( <button className="my-button" onClick={props.onClick}> {props.label} </button> ); } -
Adapter (Dostosowujący): Przekształca interfejs jednej klasy w inny, którego oczekują klienci. Umożliwia współpracę klas z niekompatybilnymi interfejsami. Używany do integracji bibliotek zewnętrznych lub API z różnymi sygnaturami metod.
// Przykład adaptera do pracy z różnymi klientami API class OldApi { getDataOld(id) { console.log(`Pobieranie danych z Old API dla id ${id}`); return { id: id, name: `Pozycja ${id}` }; } } class NewApi { fetchDataNew(itemId) { console.log(`Pobieranie danych z New API dla id elementu ${itemId}`); return { itemId: itemId, label: `Produkt ${itemId}` }; } } class ApiAdapter { constructor(apiClient) { this.apiClient = apiClient; } getData(id) { if (this.apiClient instanceof OldApi) { return this.apiClient.getDataOld(id); } else if (this.apiClient instanceof NewApi) { const newData = this.apiClient.fetchDataNew(id); return { id: newData.itemId, name: newData.label }; } throw new Error("Nieobsługiwany klient API"); } } const oldApiAdapter = new ApiAdapter(new OldApi()); console.log(oldApiAdapter.getData(1)); const newApiAdapter = new ApiAdapter(new NewApi()); console.log(newApiAdapter.getData(2)); -
Iterator (Iterator): Zapewnia sposób sekwencyjnego dostępu do elementów obiektu złożonego, nie ujawniając jego wewnętrznej reprezentacji. Używany w pętlach (
for...of) i metodach tablic (forEach,map,filter).// Przykład użycia iteratora w JavaScript const numbers = [1, 2, 3, 4, 5]; const iterator = numbers[Symbol.iterator](); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } // ... console.log(iterator.next()); // { value: undefined, done: true } for (const number of numbers) { console.log(number); } -
Dekorator (Decorator): Dynamicznie dodaje nowe obowiązki obiektowi, pozwalając na rozszerzenie funkcjonalności obiektu bez zmiany jego struktury. Używany jako HOC (Higher-Order Components) w React lub dekoratory w Angular/TypeScript.
// Przykład Higher-Order Component (HOC) w React function withLogging(WrappedComponent) { return function WithLogging(props) { console.log(`Component ${WrappedComponent.name} renderowany z propsami:`, props); return <WrappedComponent {...props} />; }; } // Zastosowanie HOC const EnhancedButton = withLogging(Button); // Użycie ulepszonego komponentu // <EnhancedButton label="Kliknij" onClick={() => console.log('Kliknięto!')} /> -
Singleton (Singleton): Gwarantuje, że klasa ma tylko jedną instancję i zapewnia globalny punkt dostępu do niej. Używany do zarządzania stanem globalnym aplikacji lub tworzenia pojedynczej instancji serwisu (np. serwisu logowania, konfiguracji).
// Przykład wzorca Singleton class ConfigManager { constructor() { if (ConfigManager.instance) { return ConfigManager.instance; } this.settings = {}; // Inicjalizacja ustawień ConfigManager.instance = this; } setSetting(key, value) { this.settings[key] = value; } getSetting(key) { return this.settings[key]; } } const config1 = new ConfigManager(); const config2 = new ConfigManager(); console.log(config1 === config2); // true (ta sama instancja) config1.setSetting('apiUrl', 'https://api.example.com'); console.log(config2.getSetting('apiUrl')); // https://api.example.com -
Metoda fabryczna (Factory Method): Definiuje interfejs do tworzenia obiektu, ale pozwala podklasom zdecydować, której klasy instancję utworzyć. Używany do tworzenia różnych typów elementów interfejsu użytkownika lub obiektów na podstawie konfiguracji.
-
Strategia (Strategy): Definiuje rodzinę algorytmów, enkapsuluje każdy z nich i czyni je wymienialnymi. Pozwala na zmienianie algorytmu niezależnie od klientów, którzy go używają. Używany do implementacji różnych podejść do sortowania danych, walidacji formularzy lub obsługi zdarzeń.
Użycie tych wzorców pomagało tworzyć bardziej uporządkowany, modułowy, testowalny i łatwy do utrzymania kod.