الگوهای طراحی یا دیزاین پترن ها راهی مناسب برای مقابله با چالشها با راهحلهای تست شده ارائه میدهند و در زمان و تلاش توسعهدهندگان صرفهجویی میکنند. در اینجا چگونه الگوهای طراحی React به ماژول های منسجم با جفت کمتر اجازه می دهند صحبت خواهیم کرد.
در دنیای پویای توسعه وب، React به عنوان یک کتابخانه قدرتمند برای ساخت رابط های کاربری ظاهر شده است. الگوهای طراحی، راه حل های قابل استفاده مجدد و اثبات شده برای مشکلات رایج، نقش مهمی در شکل دادن به برنامه های کاربردی React کارآمد و قابل نگهداری دارند.
الگوهای طراحی React دو مزیت کلیدی را در اختیار مهندسان نرم افزار قرار می دهد. اول، آنها یک راه راحت برای رسیدگی به مشکلات توسعه نرم افزار با راه حل های آزمایش شده ارائه می دهند. و دوم، ایجاد ماژول های بسیار منسجم با جفت شدن کمتر(تکرار کمتر) را بسیار آسان می کنند. در این مقاله، مهمترین الگوهای طراحی خاص React و بهترین شیوهها را شرح میدهم و سودمندی الگوهای طراحی عمومی را برای موارد استفاده مختلف در React بررسی میکنم.
الگوهای رایج طراحی React
اگرچه می توان از الگوهای طراحی یا همان Design Pattern عمومی در React استفاده کرد، اما توسعه دهندگان React بیشترین سود را از الگوهای طراحی خاص React دارند. بیایید موارد ضروری را بررسی کنیم: کامپوننت های مرتبه بالاتر، providers، کامپوننت های ترکیبی و هوک ها.
کامپوننت های مرتبه بالاتر (HOC)
از طریق props، کامپوننت های مرتبه بالاتر (HOC) منطق قابل استفاده مجدد را به کامپوننت ها ارائه می دهند. هنگامی که به یک کامپوننت تابعی موجود با یک رابط کاربری جدید نیاز داریم، از یک HOC استفاده می کنیم.
ما یک کامپوننت را با یک HOC ترکیب می کنیم تا به نتیجه دلخواه برسیم: یک کامپوننت تابعی اضافی در مقایسه با کامپوننت اصلی.
در کد، ما یک کامپوننت را در داخل یک HOC قرار می دهیم و کامپوننت مورد نظر ما را برمی گرداند:
// A simple greeting HOC.
const Greetings = ({ name, ...otherProps }) => <div {...otherProps}>Hello {name}!</div>;
const greetWithName = (BaseComponent) => (props) => (
<BaseComponent {...props} name='Toptal Engineering Blog' />
);
const Enhanced = greetWithName(Greetings)
HOC ها می توانند حاوی هر منطقی باشند. از نقطه نظر معماری، آنها در Redux رایج هستند.
الگوی طراحی Providers
با استفاده از الگوی طراحی providers، میتوانیم برنامهمان را از prop drilling به کامپوننت های تو در تو در درخت جلوگیری کنیم. ما می توانیم با Context API موجود در React به این الگو برسیم:
import React, { createContext, useContext } from 'react';
export const BookContext = createContext();
export default function App() {
return (
<BookContext.Provider value="spanish-songs">
<Book />
</BookContext.Provider>
)
}
function Book() {
const bookValue = useContext(BookContext);
return <h1>{bookValue}</h1>;
}
این مثال کد از الگوی providers نشان می دهد که چگونه می توانیم مستقیماً props را با استفاده از context به یک شی جدید ایجاد شده ارسال کنیم. context شامل ارائه دهنده و مصرف کننده state می شود. در این مثال، ارائهدهنده ما یک کامپوننت App و مصرفکننده ما یک کامپوننت Book
با استفاده از BookContext
است.
انتقال مستقیم پروپ ها از کامپوننت A به کامپوننت D به این معنی است که ما از الگوی طراحی providers استفاده می کنیم. بدون این الگو، prop drilling رخ می دهد، B و C به عنوان کامپوننت واسطه عمل می کنند.
کامپوننت Compound
کامپوننت مرکب مجموعه ای از قطعات مرتبط هستند که مکمل یکدیگر بوده و با هم کار می کنند. یک نمونه اساسی از این الگوی طراحی، یک کامپوننت کارت و عناصر مختلف آن است.
یک کامپوننت کارت تصور کنید که از تصویر، Actions
و Content
آن تشکیل شده است که به طور مشترک عملکرد آن را فراهم می کند:
import React from 'react';
const Card = ({ children }) => {
return <div className="card">{children}</div>;
};
const CardImage = ({ src, alt }) => {
return <img src={src} alt={alt} className="card-image" />;
};
const CardContent = ({ children }) => {
return <div className="card-content">{children}</div>;
};
const CardActions = ({ children }) => {
return <div className="card-actions">{children}</div>;
};
const CompoundCard = () => {
return (
<Card>
<CardImage src="https://bs-uploads.toptal.io/blackfish-uploads/public-files/Design-Patterns-in-React-Internal3-e0c0c2d0c56c53c2fcc48b2a060253c3.png" alt="Random Image" />
<CardContent>
<h2>Card Title</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
</CardContent>
<CardActions>
<button>Like</button>
<button>Share</button>
</CardActions>
</Card>
);
};
export default CompoundCard;
API برای کامپوننت مرکب وسیله ای مناسب برای بیان ارتباطات بین کامپوننت ها ارائه می دهد.
هوک ها
هوک های React به ما امکان میدهند state ها و فرآیندهای چرخه حیات یک کامپوننت را مدیریت کنیم. آنها در اوایل سال 2019 معرفی شدند، اما بسیاری از هوک های اضافی در نسخه 16.8 React در دسترس قرار گرفتند. نمونه هایی از هوک ها عبارتند از state, effect, و custom hooks.
هوک وضعیت React (useState) از دو عنصر تشکیل شده است، مقدار فعلی و تابعی که بسته به وضعیت، مقدار مورد نیاز را در صورت نیاز به روز می کند:
const [data, setData] = React.useState(initialData);
بیایید هوک state را با مثالی دقیق تر بررسی کنیم:
import React, { useState } from "react";
export default function StateInput() {
const [input, setInput] = useState("");
const inputHandler = (e) => {
setInput(e.target.value)
}
return (
<input
onChange={inputHandler}
value={input}
placeholder="Placeholder..."
/>
);
}
ما یک state را با مقدار فعلی خالی ("") می نویسیم و می توانیم مقدار آن را با استفاده از کنترل کننده onChange
به روز کنیم.
کامپوننت مبتنی بر کلاس همچنین حاوی هوک های افکت (useEffect
) هستند. عملکرد هوک useEffect مشابه متد های چرخه عمر قبلی React است: componentDidMount، componentWillMount
و componentDidUpdate
.
توسعه دهندگان React ماهر احتمالاً بر هوک ها، HOC ها، providers و کامپوننت ترکیبی تسلط دارند. با این حال، بهترین مهندسان همچنین به الگوهای طراحی عمومی، مانند پروکسی و سینگلتون، recognize هستند و تشخیص میدهند که چه زمانی از آنها در React استفاده کنند.
مقدمه ای بر الگوهای طراحی عمومی در React
الگوهای طراحی عمومی را می توان با هر زبان یا چارچوبی، بدون توجه به هر گونه تغییرات احتمالی در نیازمندی های سیستم، استفاده کرد، و کل سیستم را برای درک و نگهداری ساده تر می کند. علاوه بر این، استفاده از الگوهای طراحی اثربخشی ارتباط طراح با طراح را بهبود میبخشد: هنگام بحث در مورد طراحی سیستم، کارشناسان نرمافزار میتوانند به نام الگوی مورد استفاده برای حل یک مسئله خاص اشاره کنند و به همتایان خود اجازه دهند فوراً طراحی سطح بالا را در ذهن خود تجسم کنند.
سه دسته اصلی از الگوهای طراحی وجود دارد:
خلاقانه
ساختاری
رفتاری
این الگوها در زمینه React مفید هستند، اما از آنجایی که به طور کلی در برنامه نویسی جاوا اسکریپت استفاده می شوند، این دانش به راحتی قابل انتقال است.
الگوهای طراحی خلاقانه در React
الگوهای طراحی خلاقانه با هدف ایجاد اشیاء قابل استفاده در موقعیتهای مختلف، انعطافپذیری و قابلیت استفاده مجدد بیشتر را میسازند.
الگوی طراحی Builder
الگوی طراحی Builder با ارائه مراحلی که باید دنبال کنیم و نتیجه مراحل ترکیبی را برمی گرداند، ایجاد شی را ساده می کند:
const BuildingHouse = ({someProps}) => {
const [constructHouse, setConstructHouse] = useState({});
const completingArchitectureWork = () => {
// Add logic to modify the state of house.
};
const completingGrayStructure = () => {
// Some logic ...
};
const completingInteriorDesign = () => {
// Add some more logic ...
};
const completingFinishingWork = () => {
// Some other logic ...
};
// Returning all updated states in one state object constructHouse.
// Passing it as props on child component.
return (
<BuildHouseLand constructHouse={constructHouse} {...someProps} />
);
}
الگوی builder، تولید یک شی پیچیده را از نمایش آن جدا میکند، و اجازه میدهد بازنماییهای جایگزین با استفاده از همان روش ساختوساز ساخته شوند.
الگوی طراحی Singleton
الگوی طراحی سینگلتون راهی برای تعریف کلاسی است که فقط یک شیء از آن نمونه برداری شود. برای مثال، ممکن است از یک سینگلتون استفاده کنیم تا اطمینان حاصل کنیم که وقتی کاربر از میان روشهای مختلف ورود انتخاب میکند، تنها یک نمونه احراز هویت ایجاد میشود.
فرض کنید یک AuthComponent
به همراه authInstance
متد singleton
آن داریم که انواع را منتقل می کند و بسته به نوع تغییر state را ارائه می دهد. ما میتوانیم یک authInstance
برای سه کامپوننت داشته باشیم که به ما میگوید آیا باید کامپوننتهای احراز هویت Google
، Apple
یا Facebook
را رندر کنیم:
function AuthComponent({ authType }) {
const [currentAuth, setCurrentAuth] = useState();
const authInstance = () => {
if (authType === 'google') {
setAuth('google-authenticator')
} else if (authType === 'apple') {
setAuth('apple-authenticator')
} else if (authType === 'facebook') {
setAuth('facebook-authenticator')
} else {
// Do some extra logic.
}
}
useEffect(()=>{
authInstance()
},[authType])
return (
<div>
{currentAuth === 'google-authenticator' ? <GoogleAuth /> :
currentAuth === 'apple-authenticator' ? <AppleAuth /> :
currentAuth === 'facebook-authenticator' ? <FacebookAuth /> :
null}
</div>
)
}
function AuthInstanceUsage() {
return <AuthComponent authType='apple' />
}
یک کلاس باید یک نمونه واحد و یک نقطه ورود گلوبال واحد داشته باشد. افراد Singletons تنها زمانی باید به کار گرفته شوند که این سه شرط برآورده شوند:
تخصیص مالکیت منطقی یک نمونه غیرممکن است.
مقداردهی اولیه Lazy یک شی در نظر گرفته می شود.
دسترسی سراسری به هیچ نمونه مورد نیاز نیست.
مقداردهی اولیه Lazy یا تاخیر در مقداردهی اولیه شی یک تکنیک بهبود عملکرد است که در آن میتوانیم منتظر بمانیم تا یک شیء ایجاد شود تا زمانی که واقعاً به آن نیاز داشته باشیم.
الگوی طراحی Factory
الگوی طراحی Factory زمانی استفاده می شود که یک سوپرکلاس با چندین زیر کلاس داریم و باید یکی از زیر کلاس ها را بر اساس ورودی برگردانیم. این الگو مسئولیت نمونه سازی کلاس را از برنامه کلاینت به کلاس Factory منتقل می کند.
شما می توانید فرآیند تولید اشیاء را با استفاده از الگوی Factory ساده کنید. فرض کنید ما یک کامپوننت خودرو داریم که میتوان آن را با تغییر رفتارهای آن برای هر کامپوننت زیرخودروی شخصیسازی کرد. ما شاهد استفاده از پلی مورفیسم و رابط ها در الگوی Factory هستیم زیرا باید در زمان اجرا اشیا (ماشین های مختلف) بسازیم.
در نمونه کد زیر میتوان خودروهای abstract با پراپس های carModel
، brandName
و color
را مشاهده کرد. این کارخانه CarFactory
نام دارد، اما دارای دسته بندی هایی بر اساس شرایط نام تجاری است. برند XCar
(مثلا toyota
) خودروی خود را با ویژگیهای خاص خواهد ساخت، اما همچنان در آبسترکت CarFactory
قرار میگیرد. ما حتی میتوانیم رنگ، سطح تریم و جابجایی موتور را برای مدلها و انواع خودروهای مختلف در یک قطعه کارخانه خودرو تعریف کنیم.
ما در حال حاضر در حال پیاده سازی وراثت به عنوان طرحی از کامپوننت کلاس در حال استفاده هستیم. در این مورد، ما با ارائه موارد اضافی به آبجکتهای Car
در حال ایجاد اشیاء مختلف هستیم. چند شکلی یا پلیمورفیسم نیز رخ می دهد، زیرا کد برند و مدل هر شیء خودرو را در زمان اجرا بر اساس انواع ارائه شده در سناریوهای مختلف تعیین می کند:
const CarFactoryComponent = (carModel, brandName, color) => {
<div brandName={brandName} carModel={carModel} color={color} />
}
const ToyotaCamry = () => {
<CarFactoryComponent brandName='toyota' carModel='camry' color='black'/>
}
const FordFiesta = () => {
<CarFactoryComponent brandName='ford' carModel='fiesta' color='blue'/>
}
متد های کارخانه معمولاً توسط یک چارچوب معماری مشخص میشوند و سپس توسط کاربر چارچوب پیادهسازی میشوند.
الگوهای طراحی ساختاری در React
الگوهای طراحی ساختاری می تواند به توسعه دهندگان React کمک کند تا روابط بین کامپوننت مختلف را تعریف کنند و به آنها امکان گروه بندی کامپوننت ها و ساده سازی ساختارهای بزرگتر را می دهد.
الگوی طراحی Facade
الگوی طراحی Facade با ایجاد یک API واحد، تعامل با کامپوننت مختلف را ساده می کند. پنهان کردن فعل و انفعالات اساسی کد را خواناتر می کند. الگوی Facade همچنین می تواند به گروه بندی عملکردهای عمومی در یک زمینه خاص تر کمک کند و به ویژه برای سیستم های پیچیده با الگوهای تعامل مفید است.
یک مثال، یک بخش پشتیبانی که با چندین مسئولیت را در نظر بگیرید، مانند تأیید اینکه آیا برای یک کالا صورتحساب دریافت شده است یا خیر، یک تیکت پشتیبانی دریافت شده است یا سفارش داده شده است.
فرض کنید یک API داریم که شامل متدهای دریافت، ارسال و حذف است:
class FacadeAPI {
constructor() { ... }
get() { ... }
post() { ... }
delete() { ... }
}
اکنون اجرای این نمونه الگوی Facade را به پایان می رسانیم:
import { useState, useEffect } from 'react';
const Facade = () => {
const [data, setData] = useState([]);
useEffect(()=>{
// Get data from API.
const response = axios.get('/getData');
setData(response.data)
}, [])
// Posting data.
const addData = (newData) => {
setData([...data, newData]);
}
// Using remove/delete API.
const removeData = (dataId) => {
// ...logic here...
}
return (
<div>
<button onClick={addData}>Add data</button>
{data.map(item=>{
<>
<h2 key={item.id}>{item.id}</h2>
<button onClick={() => removeData(item.id)}>Remove data</button>
</>
})}
</div>
);
};
export default Facade;
به یک محدودیت مهم الگوی Facade توجه کنید: یک زیرمجموعه از پایگاه کلاینت برای دستیابی به عملکرد کلی یک زیرسیستم پیچیده به یک رابط کارآمد نیاز دارد.
الگوی طراحی دکوراتور
الگوی طراحی دکوراتور از اشیاء لایهبندی و دسته بندی برای افزودن رفتار به اشیاء موجود بدون تغییر عملکرد درونی آنها استفاده میکند. به این ترتیب یک کامپوننت می تواند با تعداد نامتناهی کامپوننت لایه بندی یا پیچیده شود. همه کامپوننت بیرونی می توانند رفتار خود را فورا تغییر دهند اما رفتار کامپوننت پایه تغییر نمی کند. کامپوننت پایه یک تابع خالص است که فقط یک کامپوننت جدید را بدون چیز خاصی برمی گرداند.
HOC نمونه ای از این الگو است. (بهترین مورد استفاده برای الگوهای طراحی دکوراتور، memo است، اما در اینجا به آن پرداخته نمی شود، زیرا نمونه های خوب زیادی در دسترس است.)
بیایید الگوهای دکوراتور را در React بررسی کنیم:
export function canFly({ targetAnimal }) {
if (targetAnimal) {
targetAnimal.fly = true;
}
}
// Example 1.
@canFly()
// We can define a list of decorators here to any class or functional components.
class Eagle(){
// ...logic here...
}
// Example 2
const Eagle = () => {
@canFly()
function eagleCanFly() {
// ...logic here...
}
}
در این مثال، canFly متدی است که می توان آن را در هر مکانی بدون هیچ گونه چیز اضافی استفاده کرد. میتوانیم دکوراتورها را در بالای هر کامپوننت کلاس تعریف کنیم، یا میتوانیم از آنها در توابعی که در کلاس یا کامپوننت توابعی تعریف میشوند استفاده کنیم.
دکوراتورها یک الگوی طراحی کد قدرتمند هستند که به شما امکان میدهد کامپوننتهای React تمیزتر و قابل نگهداریتر بنویسید، اما من همچنان HOC را به دکوراتورهای کلاس ترجیح میدهم. از آنجایی که دکوراتورها هنوز یک پیشنهاد ECMAScript هستند، ممکن است در طول زمان تغییر کنند. بنابراین، با احتیاط استفاده کنید.
الگوی طراحی Bridge
الگوی طراحی پل در هر برنامه یک اپلیکیشن فرانت اند بسیار قدرتمند است زیرا یک انتزاع را از اجرای آن جدا می کند تا این دو بتوانند به طور مستقل تغییر کنند.
ما از الگوهای طراحی پل زمانی استفاده میکنیم که میخواهیم پیادهسازیهای زمان اجرا الزامآوری داشته باشیم، کلاسها را در نتیجه یک رابط جفت شده و پیادهسازیهای متعدد داریم، میخواهیم یک پیادهسازی را بین چندین شی به اشتراک بگذاریم، یا زمانی که نیاز به ترسیم سلسله مراتب کلاسهای متعامد داریم.
فرض کنید چند تا تلویزیون و ریموت برند متفاوتی داریم. هر کنترل از راه دور به نام تجاری اختصاصی خود ارجاع داده می شود. تلویزیون سامسونگ باید به کنترل از راه دور سامسونگ ارجاع داده شود. یک کنترل از راه دور سونی با آن کار نمی کند زیرا حتی اگر دارای دکمه های مشابهی باشد (به عنوان مثال، روشن، خاموش، کانال بالا و کانال پایین)، اجرای آن متفاوت است.
// Just a path to remotes.
import { remote1, remote2, remote3 } from "./generic-abstraction";
// Just a path to TVs.
import { TV1, TV2, TV3 } from "./implementation-of-abstraction";
// This function is a bridge of all these remotes and TVs.
const BridgeTV = () => {
// Some states calculate the type of remote so that we can return TV types.
return (
<TVGraphicsChanger
{...someBridgeProps}
// Some hidden logic to abstract the remote types and return a TV.
uiComponent={
remote1 ? <TV1 /> : remote2 ? <TV2 /> : remote3 ? <TV3 /> : null
}
/>
);
};
در الگوی طراحی پل، باید به یاد داشته باشیم که مرجع باید صحیح باشد و تغییر صحیح را منعکس کند.
الگوی طراحی پروکسی
الگوی طراحی پراکسی از پروکسی استفاده می کند که به عنوان جانشین یا مکان نگهدار هنگام دسترسی به یک شی عمل می کند. یک مثال روزمره از پروکسی، کارت اعتباری است که نشان دهنده پول نقد فیزیکی یا پول در یک حساب بانکی است.
بیایید این الگو را در عمل ببینیم و نمونه مشابهی را کدگذاری کنیم که در آن وجه را انتقال میدهیم و یک برنامه پرداخت موجودی موجود در حساب بانکی ما را بررسی میکند:
const thirdPartyAPI = (accountId) => { ... }
// The proxy function.
const checkBalance = accountId => {
return new Promise(resolve => {
// Some conditions.
thirdPartyAPI(accountId).then((data) => { ... });
});
}
// Test run on proxy function.
transferFunds().then(someAccountId => {
// Using proxy before transferring or money/funds.
if(checkBalance(someAccountId)) { ... }
}).catch(error=> console.log('Payment failed', error))
در کد ما، تأیید موجودی حساب توسط برنامه پرداخت به عنوان پروکسی عمل می کند.
الگوهای طراحی رفتاری در React
الگوهای طراحی رفتاری بر ارتباط بین کامپوننت مختلف تمرکز می کنند و به دلیل ماهیت کامپوننت محوری آن، آنها را برای React مناسب می کند.
الگوی طراحی state
الگوی طراحی state معمولاً برای افزودن واحدهای اساسی کپسولاسیون (state) در برنامه نویسی کامپوننت استفاده می شود. نمونه ای از الگوی state تلویزیونی است که رفتار آن از طریق کنترل از راه دور تغییر می کند.
بر اساس وضعیت دکمه کنترل از راه دور (روشن یا خاموش)، وضعیت تلویزیون مطابق با آن تغییر می کند. به طور مشابه، در React، میتوانیم وضعیت یک کامپوننت را بر اساس ویژگیهای آن یا شرایط دیگر تغییر دهیم.
هنگامی که وضعیت یک شی تغییر می کند، رفتار آن تغییر می کند:
// Without state property.
<WithoutState otherProps={...otherProps} state={null}/>
// With state property.
<WithState otherProps={...otherProps} state={...state} />
کامپوننت WithState
در این نمونههای کد متفاوت عمل میکند، بسته به اینکه چه زمانی یک State prop ارائه میکنیم و چه زمانی برای آن null ارائه میکنیم. بنابراین کامپوننت ما با توجه به ورودی ما حالت یا رفتار خود را تغییر می دهد، به همین دلیل است که الگوی طراحی state را یک الگوی رفتاری می نامیم.
الگوی طراحی Command
الگوی طراحی Command یک الگوی عالی برای طراحی سیستم های تمیز و جدا شده است. این الگو به ما این امکان را می دهد که در آینده یک منطق تجاری را اجرا کنیم. من به ویژه می خواهم روی الگوی Command تمرکز کنم زیرا معتقدم که الگوی اصلی Redux است. بیایید ببینیم که چگونه می توان از الگوی Command با یک ریدیوسر Redux استفاده کرد:
const initialState = {
filter: 'SHOW_ALL',
arr: []
}
function commandReducer(state = initialState, action) {
switch (action.type) {
case 'SET_FILTER': { ... }
case 'ADD_TODO': { ... }
case 'EDIT_TODO': { ... }
default:
return state
}
}
در این مثال، ریدیوسر Redux شامل موارد متعددی است، که توسط موقعیتهای مختلف ایجاد میشوند، که رفتارهای متفاوتی را برمیگردانند.
الگوی طراحی Observer
الگوی طراحی Observer به آبجکت ها اجازه می دهد تا در تغییرات در وضعیت یک شی دیگر مشترک شوند و به طور خودکار هنگام تغییر وضعیت اعلان دریافت کنند. این الگو، اشیاء مشاهدهشده را از شی Observer جدا میکند، بنابراین ماژولار بودن و انعطافپذیری را ارتقا میدهد.
در معماری Model-View-Controller (MVC)، الگوی Observer معمولاً برای انتشار تغییرات از مدل به Faced ها استفاده میشود و Faced ها را قادر میسازد تا وضعیت بهروز شده مدل را بدون نیاز به دسترسی مستقیم به دادههای داخلی مدل مشاهده و نمایش دهند. :
const Observer = () => {
useEffect(() => {
const someEventFunc = () => { ... }
// Add event listener.
documentListener('EVENT_TRIGGER_NAME', () => { ... })
return () => {
// Remove event listener.
documentListener('EVENT_TRIGGER_NAME', () => { ... })
}
}, [])
}
شی Observer ارتباط را با معرفی اشیاء "observer" و "subject" توزیع می کند، در حالی که الگوهای دیگر مانند واسطه و شیء آن ارتباط بین اشیاء دیگر را در بر می گیرند. ایجاد مشاهدهپذیرهای قابل استفاده مجدد آسانتر از ایجاد واسطههای قابل استفاده مجدد است، اما یک میانجی میتواند از یک مشاهدهگر برای ثبت دینامیکی هم الگوی خود و برقراری ارتباط با آنها استفاده کند.
الگوی طراحی استراتژی
الگوی طراحی استراتژی راهی برای تغییر برخی رفتارها به صورت پویا از بیرون بدون تغییر کامپوننت پایه است. یک خانواده الگوریتم را تعریف می کند، هر کدام را کپسوله می کند و آنها را قابل تعویض می کند. این استراتژی به کامپوننت والد اجازه می دهد تا مستقل از فرزندی که از آن استفاده می کند تغییر کند. می توانید انتزاع را در یک رابط قرار دهید و جزئیات پیاده سازی را در کلاس های مشتق شده قرار دهید:
const Strategy = ({ children }) => {
return <div>{children}</div>;
};
const ChildComp = () => {
return <div>ChildComp</div>;
};
<Strategy children={<ChildComp />} />;
از آنجایی که اصل باز-بسته استراتژی غالب طراحی شی گرا است، الگوی طراحی استراتژی یکی از راه های انطباق با اصول OOP و دستیابی به انعطاف پذیری زمان اجرا است.
الگوی طراحی Memento
الگوی طراحی Memento حالت داخلی یک شی را به تصویر میکشد و آن را بیرونی میکند، به طوری که میتوان آن را بدون شکستن کپسوله بازیابی کرد. ما نقش های زیر را در الگوی طراحی Memento داریم:
آبجکتی که می تواند خود را نجات دهد، سازنده است.
مراقب از شرایطی که سازنده باید خود را نجات دهد و بازیابی کند، آگاه است.
مموری ها در یک جعبه قفلی نگهداری میشوند که توسط مراقب نگهداری میشود و توسط سازنده نوشته و خوانده میشود.
بیایید با بررسی یک مثال کد آن را یاد بگیریم. الگوی یادگاری از chrome.storage API برای ذخیره و بارگیری دادهها استفاده میکند. در مثال مفهومی زیر، دادهها را در تابع setState
و دادهها را در تابع getState
بارگذاری میکنیم:
class Memento {
// Stores the data.
setState(){ ... }
// Loads the data.
getState() { ... }
}
اما مورد استفاده واقعی در React به شرح زیر است:
const handler = () => ({
organizer: () => {
return getState(); // Organizer.
},
careTaker: (circumstance, type) => {
return type === "B" && circumstance === "CIRCUMSTANCE_A"
? {
condition: "CIRCUMSTANCE_A",
state: getState().B,
}
: {
condition: "CIRCUMSTANCE_B",
state: getState().B,
};
//
},
memory: (param) => {
const state = {};
// Logic to update state based on param.
// Send param as well to memorize the state based on.
// Circumstances for careTaker function.
setState({ param, ...state }); // Memories.
},
});
در این مثال انتزاعی، getState را در سازماندهنده (در کنترلکننده)، و زیرمجموعهای از حالت آن را در دو شاخه منطقی داخل عبارت return careTaker برمیگردانیم.
یک الگوی دیگری نیز وجود دارد که ما در الگوی کامپوننت Headless بررسی کردیم.
چرا الگوهای React مهم هستند؟
اگرچه الگوها راهحلهای آزمودهشدهای را برای مشکلات تکراری ارائه میدهند، مهندسان نرمافزار باید قبل از اعمال هر الگوی طراحی، از مزایا و معایب آن آگاه باشند.
مهندسان به طور معمول از الگوهای طراحی Hook ، Hook های سفارشی و Context API استفاده می کنند، اما درک و به کارگیری الگوهای طراحی React که من توضیح دادم، مهارت های فنی اساسی توسعه دهندگان React را تقویت می کند و به زبان های بسیاری ارائه می شود. از طریق این الگوهای عمومی، مهندسان React این اختیار را دارند که به جای استفاده از یک الگوی خاص برای برآورده کردن نیازها یا رسیدگی به یک مشکل واحد، نحوه عملکرد کد را از نظر معماری توصیف کنند.
نتیجه
در نتیجه، الگوهای طراحی React ابزارهای ضروری برای توسعه دهندگانی هستند که هدفشان ایجاد برنامه های کاربردی قوی، مقیاس پذیر و قابل نگهداری است. با گنجاندن این الگوها در گردش کار توسعه خود، می توانید با چالش های رایج مقابله کنید، عملکرد را بهینه کنید و تجربه کلی کاربر را ارتقا دهید.