Разкажи за шаблонното проектиране, което си използвал в разработката си.
sobes.tech AI
Отговор от AI
Използвах следните шаблони за дизайн:
-
MVC (Model-View-Controller): Разделяне на логиката на приложението. Моделът управлява данните, изгледът отговаря за показването, а контролерът обработва взаимодействието с потребителя и актуализира Модела или Изгледа. Използвах го в Angular, където компонентите частично реализират тази идеология.
-
MVVM (Model-View-ViewModel): Подобен на MVC, но въвежда ViewModel като посредник между Изгледа и Модела. ViewModel съдържа бизнес логиката за показване на данни и обработка на действията на потребителя. Активно го използвах с React и Vuex (Vue), където състоянието на приложението се управлява централизирано.
// Пример за реактивност с използване на useState import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); // useState е част от механизма MVVM const increment = () => { setCount(count + 1); // актуализиране на състоянието чрез setter }; return ( <div> <p>Брояч: {count}</p> {/* показване на състоянието */} <button onClick={increment}>Увеличи</button> {/* действие, влияещо на състоянието */} </div> ); } -
Компонентен модел: Разделя потребителския интерфейс на независими, повторно използваеми компоненти. Всяки компонент инкапсулира своята логика, структура и стилове. Този модел е основата на съвременните рамки като React, Vue и Angular.
// Пример за прост React компонент function Button(props) { return ( <button className="my-button" onClick={props.onClick}> {props.label} </button> ); } -
Адаптер (Adapter): Преобразува интерфейса на един клас в друг, който очакват клиентите. Позволява съвместната работа на класове с несъвместими интерфейси. Използвах го за интеграция на външни библиотеки или API с различни сигнатури на методите.
// Пример за адаптер за различни API клиенти class OldApi { getDataOld(id) { console.log(`Получаване на данни от Old API за id ${id}`); return { id: id, name: `Елемент ${id}` }; } } class NewApi { fetchDataNew(itemId) { console.log(`Извличане на данни от New API за елемент id ${itemId}`); return { itemId: itemId, label: `Продукт ${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("Несъвместим API клиент"); } } const oldApiAdapter = new ApiAdapter(new OldApi()); console.log(oldApiAdapter.getData(1)); const newApiAdapter = new ApiAdapter(new NewApi()); console.log(newApiAdapter.getData(2)); -
Итератор (Iterator): Осигурява начин за последователен достъп до елементите на сложен обект, без да разкрива вътрешното му представяне. Използва се в цикли (
for...of) и методи на масиви (forEach,map,filter).// Пример за използване на итератор в 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...of for (const number of numbers) { console.log(number); } -
Декоратор (Decorator): Динамично добавя нови отговорности към обект, позволявайки разширяване на функционалността му без промяна на структурата му. Използвах го като HOC (Higher-Order Components) в React или декоратори в Angular/TypeScript.
// Пример за HOC в React function withLogging(WrappedComponent) { return function WithLogging(props) { console.log(`Компонент ${WrappedComponent.name} е рендериран с props:`, props); return <WrappedComponent {...props} />; }; } // Използване на HOC const EnhancedButton = withLogging(Button); // Използване на подобрения компонент // <EnhancedButton label="Кликни" onClick={() => console.log('Клик!')} /> -
Одиночка (Singleton): Гарантира, че класът има само един екземпляр и предоставя глобален достъп до него. Използвах го за управление на глобалното състояние на приложението или за създаване на единствен екземпляр на услуга (например, услуга за логване, конфигурация).
// Пример за шаблон Одиночка class ConfigManager { constructor() { if (ConfigManager.instance) { return ConfigManager.instance; } this.settings = {}; // Инициализация на настройки 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 (един и същият екземпляр) config1.setSetting('apiUrl', 'https://api.example.com'); console.log(config2.getSetting('apiUrl')); // https://api.example.com -
Фабричен метод (Factory Method): Определя интерфейс за създаване на обект, но подкласовете решават кой обект да създадат. Използвах го за създаване на различни типове елементи на потребителския интерфейс или обекти въз основа на конфигурация.
-
Стратегия (Strategy): Определя семейство алгоритми, капсулира всеки от тях и ги прави взаимозаменяеми. Позволява алгоритъмът да варира независимо от клиентите, които го използват. Използвах го за реализиране на различни подходи към сортирането на данни, валидирането на форми или обработката на събития.
Използването на тези шаблони помогна за създаването на по-структуриран, модулен, тестируем и поддържан код.