طراحی نرم افزار با استفاده از OOP + FP – قسمت 1
Summarize this content to 400 words in Persian Lang
طراحی نرم افزار با استفاده از OOP + FP – قسمت 1
بیایید راه حل های دیوانه کننده ای را با ترکیب OOP و FP انجام دهیم 🙂
ایده این مقاله این است که چندین الگو را خلاصه کند و مثال هایی از نحوه حل موارد خاص با استفاده از برنامه نویسی شی گرا ارائه دهد.OOP) و برنامه نویسی تابعی (FP) برای به دست آوردن راه حل های بهتر.
در طول زندگی حرفه ای خود، می توانیم بین پارادایم های برنامه نویسی مختلف حرکت کنیم. هر کدام نقاط قوت و ضعف خود را دارند و بیش از دو مورد وجود دارد. ما OOP و FP را بررسی خواهیم کرد زیرا آنها بیشترین استفاده را دارند. نکته کلیدی همیشه این است که بدانید چه زمانی و چگونه هر کدام را برای به دست آوردن راه حل های بهتر اعمال کنید.
بیایید یک بررسی سریع از ایده ها/مفاهیم اصلی هر پارادایم داشته باشیم
ویژگی های برنامه نویسی شی گرا
کپسوله سازی: پنهان کردن جزئیات داخلی و افشای تنها موارد ضروری.
وراثت: ایجاد کلاس های جدید بر اساس کلاس های موجود.
پلی مورفیسم: کلاس های مختلف را می توان از طریق یک رابط مشترک درمان کرد.
انتزاع – مفهوم – برداشت: ساده سازی سیستم های پیچیده با پنهان کردن جزئیات غیر ضروری.
صحافی پویا: تعیین اینکه کدام متد در زمان اجرا به جای زمان کامپایل فراخوانی شود.
ارسال پیام: اشیا با ارسال و دریافت پیام با یکدیگر ارتباط برقرار می کنند.
ویژگی های برنامه نویسی کاربردی
تغییرناپذیری: داده ها پس از ایجاد تغییر نمی کنند.
توابع خالص: همیشه یک نتیجه را برای ورودی های یکسان برگردانید.
ترکیب تابع: ترکیب توابع ساده برای ایجاد توابع پیچیده تر.
بازگشت: حل مسائل با تقسیم کردن آنها به موارد کوچکتر همان مشکل.
نظم بالا: توابعی که توابع دیگر را به عنوان آرگومان می گیرند یا توابع را به عنوان نتیجه برمی گردند.
شفافیت ارجاعی: یک عبارت را می توان بدون تغییر در رفتار برنامه با مقدار آن جایگزین کرد.
اکنون که موضوعات پارادایم های اصلی را مرور کردیم، آماده بررسی برخی از الگوهای بوی رایج هستیم، اما قبل از آن، می خواهم مقدمات برتر خود را هنگام کدنویسی ارائه کنم.
مقدار نحو استفاده شده را (تا حد امکان) به حداقل برسانید.
از تعریف پارامترهای غیر ضروری خودداری کنید.
توابع/روش ها را کوچک نگه دارید و روی تک منظوره متمرکز کنید (در صورت امکان درون خطی).
طراحی با استفاده از تغییر ناپذیری و تغییرپذیری ترک فقط برای موارد خاص (کپسوله شده).
از نام های توصیفی برای توابع، متدها، کلاس ها و ثابت ها استفاده کنید.
از استفاده از کلمات کلیدی تکرار داخلی خودداری کنید (مگر اینکه استفاده از یک تابع مرتبه بالا راه حل را بدتر کند).
هزینه محاسباتی را همیشه در نظر داشته باشید.
کد باید خودش توضیح دهد.
توجه داشته باشید: من از *NodeJs *برای این مثال ها به دلیل امکانات استفاده خواهم کرد، اگرچه این ایده ها باید به راحتی با استفاده از هر زبان برنامه نویسی دیگری که به ما امکان استفاده از پارادایم های OOP و FP را می دهد، پیاده سازی شوند.
Complex if then else و سوئیچ موارد
چندین بار کدی با شرایط تو در تو یا موارد سوئیچ می بینم که درک و حفظ منطق را سخت می کند
const reactByAction = action => {
switch (action.type) {
case ‘A’:
return calculateA(action)
case ‘B’:
return calculateB(action)
case ‘C’:
return calculateC(action)
default:
return // Do nothing
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
در اینجا می توانیم بین دو رویکرد یکی را انتخاب کنیم. ما میتوانیم تصمیم بگیریم از *Polymorphism *کلاسیک در OOP استفاده کنیم و مسئولیت آن محاسبات را به هر نمایش عملی محول کنیم.
const reactByAction = action => action.calculate()
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
جایی که هر عمل می داند چگونه باید محاسبه شود
` اکشن کلاس انتزاعی {محاسبه انتزاعی()}
class ActionA extends Action {
calculate() {
…
}
}
class ActionB extends Action {
calculate() {
…
}
}
…`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
اما، در واقع، گاهی اوقات تصور میشد که راهحلهای ما مانند اشیاء نمونه رفتار نمیشوند. در این حالت می توانیم از نگاشت شیء کلاسیک و شفافیت ارجاعی، که مانند یک سوئیچ غیر مستقیم کار می کند، اما به ما اجازه می دهد تا موارد پیچیده را حذف کنیم
const calculateAction = {A: action => …,B: action => …,C: action => …,}
const reactByAction = action => calculateAction?.[action.type](action)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
البته، این یک محدودیت دارد، هر بار که شما نیاز به “شناسایی” یک نوع عمل دارید، باید یک نگاشت شی ثابت جدید ایجاد کنید.
هر راه حلی که انتخاب میشود، همیشه میخواهید آن را ساده نگه دارید، مسئولیتها را به بهترین شکل ممکن محول کنید، تعداد خطوط راهحل خود را توزیع کنید و از یک نام توضیحی خوب برای سادهسازی درک/مقیاسپذیری استفاده کنید.
اگر نمی توانید یک نمونه از هر عمل ارائه دهید، پیشنهاد می کنم پیاده سازی ذوب شده را اجرا کنید
` کلاس Handler {سازنده() {this.actionMap = {A: action => new ActionA(action)،B: action => new ActionB(action)C: action => new ActionC(action)};}
reactByAction = action => this.actionMap[action.type]?.(action).calculate()
}`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تعاریف پارامترهای غیر ضروری
گاهی اوقات، ما توابع/روش هایی را با پارامترهای زیادی که می توانند از یک زمینه مشترک مشتق شوند، پیاده سازی می کنیم، در اینجا یک مثال ساده وجود دارد.`const totalPrice = (قیمت، نرخ مالیات، نرخ تخفیف) => {const taxAmount = قیمت * نرخ مالیاتconst discountAmount = قیمت * نرخ تخفیفقیمت بازگشت + مالیات مبلغ – تخفیف مبلغ}
const totalForRegularCustomer = price => totalPrice(price, 0.1, 0)
const totalForPremiumCustomer = price => totalPrice(price, 0.1, 0.1)`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تعاریف درست هستند، اما میتوانیم تعداد مسئولیتها را با جدا کردن تابع، با استفاده از مفهوم Currying و کلاسها برای محصور کردن جزئیات و ایجاد روشهای تخصصیتر سادهسازی کنیم.
در اینجا یک پیشنهاد ساده با استفاده از OOP وجود دارد
` مشتری کلاس {taxAmount = قیمت => قیمت * this.taxRate()discountAmount = قیمت => قیمت * this.discountRate()totalPrice = قیمت => قیمت + this.taxAmount(price) – this.discountAmount(price)}
class RegularCustomer extends Customer {
taxRate = () => 0.1
discountRate = () => 0
}
class PremiumCustomer extends Customer {
taxRate = () => 0.05
discountRate = () => 0.1
}`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
توجه داشته باشید که من از روش الگوی الگو استفاده کردم 🙂
و در اینجا گزینه دیگری با استفاده از FP وجود دارد
` مالیات بر هزینه = نرخ مالیات => قیمت => قیمت * نرخ مالیاتconst discount = discountRate => price => price * discountRate
const regularTaxAmout = tax(0.1)
const premiumTaxAmout = tax(0.05)
const regularDiscount = discount(0)
const premiumDiscount = discount(0.1)
const calculationWithRates = (taxRateFunc, discountRateFunc) => price =>
price + taxRateFunc(price) – discountRateFunc(price)
const totalForRegularCustomer = calculationWithRates(regularTaxAmout, regularDiscount)
const totalForPremiumCustomer = calculationWithRates(premiumTaxAmout, premiumDiscount)`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
توجه داشته باشید که من توابع را برای ارائه انتزاعات بیشتر و افزایش قابلیت اعلان انجام می دهم
باز هم، ما می توانیم هر دو پارادایم را در یک طرح ترکیبی ترکیب کنیم 🙂
` مشتری کلاس {سازنده (نرخ مالیات، نرخ تخفیف) {this.taxRate = taxRatethis.discountRate = discountRate}
totalPrice = price => price + this.calculateTax(price) – this.calculateDiscount(price)
calculateTax = price => price * this.taxRate
calculateDiscount = price => price * this.discountRate
}
const regularCustomer = new Customer(0.1, 0)
const premiumCustomer = new Customer(0.05, 0.1)
const total = customer => customer.totalPrice
const totalForRegularCustomer = total(regularCustomer)
const totalForPremiumCustomer = total(premiumCustomer)`
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
روش/عملکردهای طولانی بیش از حد
کلاسیک دیگری که من به دیدن آن عادت دارم روش ها و عملکردهایی با مسئولیت های زیاد، بیش از حد طولانی، با قراردادهای سخت کدگذاری شده و قوانین تجاری پیچیده است.
` const tokenize = کد => {let tokens = []اجازه دهید currentToken = ”بگذارید currentType = null
for (let i = 0; i < code.length; i++) {
let char = code[i]
if (char === ‘ ‘ || char === ‘
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
‘ || کاراکتر === ”) {if (currentToken !== ”) {tokens.push({ type: currentType، مقدار: currentToken })CurrentToken = ”CurrentType = null}} else if (char === ‘+’ || char === ‘-‘ || char === ‘*’ || char === “https://dev.to/”) {if (currentToken !== ”) {tokens.push({ type: currentType، مقدار: currentToken })currentToken = ”}tokens.push({ type: ‘operator’, value: char });} else if (char >= ‘0’ && char = ‘a’ && char = ‘A’ && char
طراحی نرم افزار با استفاده از OOP + FP – قسمت 1
بیایید راه حل های دیوانه کننده ای را با ترکیب OOP و FP انجام دهیم 🙂
ایده این مقاله این است که چندین الگو را خلاصه کند و مثال هایی از نحوه حل موارد خاص با استفاده از برنامه نویسی شی گرا ارائه دهد.OOP) و برنامه نویسی تابعی (FP) برای به دست آوردن راه حل های بهتر.
در طول زندگی حرفه ای خود، می توانیم بین پارادایم های برنامه نویسی مختلف حرکت کنیم. هر کدام نقاط قوت و ضعف خود را دارند و بیش از دو مورد وجود دارد. ما OOP و FP را بررسی خواهیم کرد زیرا آنها بیشترین استفاده را دارند. نکته کلیدی همیشه این است که بدانید چه زمانی و چگونه هر کدام را برای به دست آوردن راه حل های بهتر اعمال کنید.
بیایید یک بررسی سریع از ایده ها/مفاهیم اصلی هر پارادایم داشته باشیم
ویژگی های برنامه نویسی شی گرا
کپسوله سازی: پنهان کردن جزئیات داخلی و افشای تنها موارد ضروری.
وراثت: ایجاد کلاس های جدید بر اساس کلاس های موجود.
پلی مورفیسم: کلاس های مختلف را می توان از طریق یک رابط مشترک درمان کرد.
انتزاع – مفهوم – برداشت: ساده سازی سیستم های پیچیده با پنهان کردن جزئیات غیر ضروری.
صحافی پویا: تعیین اینکه کدام متد در زمان اجرا به جای زمان کامپایل فراخوانی شود.
ارسال پیام: اشیا با ارسال و دریافت پیام با یکدیگر ارتباط برقرار می کنند.
ویژگی های برنامه نویسی کاربردی
تغییرناپذیری: داده ها پس از ایجاد تغییر نمی کنند.
توابع خالص: همیشه یک نتیجه را برای ورودی های یکسان برگردانید.
ترکیب تابع: ترکیب توابع ساده برای ایجاد توابع پیچیده تر.
بازگشت: حل مسائل با تقسیم کردن آنها به موارد کوچکتر همان مشکل.
نظم بالا: توابعی که توابع دیگر را به عنوان آرگومان می گیرند یا توابع را به عنوان نتیجه برمی گردند.
شفافیت ارجاعی: یک عبارت را می توان بدون تغییر در رفتار برنامه با مقدار آن جایگزین کرد.
اکنون که موضوعات پارادایم های اصلی را مرور کردیم، آماده بررسی برخی از الگوهای بوی رایج هستیم، اما قبل از آن، می خواهم مقدمات برتر خود را هنگام کدنویسی ارائه کنم.
مقدار نحو استفاده شده را (تا حد امکان) به حداقل برسانید.
از تعریف پارامترهای غیر ضروری خودداری کنید.
توابع/روش ها را کوچک نگه دارید و روی تک منظوره متمرکز کنید (در صورت امکان درون خطی).
طراحی با استفاده از تغییر ناپذیری و تغییرپذیری ترک فقط برای موارد خاص (کپسوله شده).
از نام های توصیفی برای توابع، متدها، کلاس ها و ثابت ها استفاده کنید.
از استفاده از کلمات کلیدی تکرار داخلی خودداری کنید (مگر اینکه استفاده از یک تابع مرتبه بالا راه حل را بدتر کند).
هزینه محاسباتی را همیشه در نظر داشته باشید.
کد باید خودش توضیح دهد.
توجه داشته باشید: من از *NodeJs *برای این مثال ها به دلیل امکانات استفاده خواهم کرد، اگرچه این ایده ها باید به راحتی با استفاده از هر زبان برنامه نویسی دیگری که به ما امکان استفاده از پارادایم های OOP و FP را می دهد، پیاده سازی شوند.
Complex if then else و سوئیچ موارد
چندین بار کدی با شرایط تو در تو یا موارد سوئیچ می بینم که درک و حفظ منطق را سخت می کند
const reactByAction = action => {
switch (action.type) {
case 'A':
return calculateA(action)
case 'B':
return calculateB(action)
case 'C':
return calculateC(action)
default:
return // Do nothing
}
}
در اینجا می توانیم بین دو رویکرد یکی را انتخاب کنیم. ما میتوانیم تصمیم بگیریم از *Polymorphism *کلاسیک در OOP استفاده کنیم و مسئولیت آن محاسبات را به هر نمایش عملی محول کنیم.
const reactByAction = action => action.calculate()
جایی که هر عمل می داند چگونه باید محاسبه شود
` اکشن کلاس انتزاعی {
محاسبه انتزاعی()
}
class ActionA extends Action {
calculate() {
...
}
}
class ActionB extends Action {
calculate() {
...
}
}
...`
اما، در واقع، گاهی اوقات تصور میشد که راهحلهای ما مانند اشیاء نمونه رفتار نمیشوند. در این حالت می توانیم از نگاشت شیء کلاسیک و شفافیت ارجاعی، که مانند یک سوئیچ غیر مستقیم کار می کند، اما به ما اجازه می دهد تا موارد پیچیده را حذف کنیم
const calculateAction = {
A: action => ...,
B: action => ...,
C: action => ...,
}
const reactByAction = action => calculateAction?.[action.type](action)
البته، این یک محدودیت دارد، هر بار که شما نیاز به “شناسایی” یک نوع عمل دارید، باید یک نگاشت شی ثابت جدید ایجاد کنید.
هر راه حلی که انتخاب میشود، همیشه میخواهید آن را ساده نگه دارید، مسئولیتها را به بهترین شکل ممکن محول کنید، تعداد خطوط راهحل خود را توزیع کنید و از یک نام توضیحی خوب برای سادهسازی درک/مقیاسپذیری استفاده کنید.
اگر نمی توانید یک نمونه از هر عمل ارائه دهید، پیشنهاد می کنم پیاده سازی ذوب شده را اجرا کنید
` کلاس Handler {
سازنده() {
this.actionMap = {
A: action => new ActionA(action)،
B: action => new ActionB(action)
C: action => new ActionC(action)
};
}
reactByAction = action => this.actionMap[action.type]?.(action).calculate()
}`
تعاریف پارامترهای غیر ضروری
گاهی اوقات، ما توابع/روش هایی را با پارامترهای زیادی که می توانند از یک زمینه مشترک مشتق شوند، پیاده سازی می کنیم، در اینجا یک مثال ساده وجود دارد.
`
const totalPrice = (قیمت، نرخ مالیات، نرخ تخفیف) => {
const taxAmount = قیمت * نرخ مالیات
const discountAmount = قیمت * نرخ تخفیف
قیمت بازگشت + مالیات مبلغ – تخفیف مبلغ
}
const totalForRegularCustomer = price => totalPrice(price, 0.1, 0)
const totalForPremiumCustomer = price => totalPrice(price, 0.1, 0.1)`
تعاریف درست هستند، اما میتوانیم تعداد مسئولیتها را با جدا کردن تابع، با استفاده از مفهوم Currying و کلاسها برای محصور کردن جزئیات و ایجاد روشهای تخصصیتر سادهسازی کنیم.
در اینجا یک پیشنهاد ساده با استفاده از OOP وجود دارد
` مشتری کلاس {
taxAmount = قیمت => قیمت * this.taxRate()
discountAmount = قیمت => قیمت * this.discountRate()
totalPrice = قیمت => قیمت + this.taxAmount(price) – this.discountAmount(price)
}
class RegularCustomer extends Customer {
taxRate = () => 0.1
discountRate = () => 0
}
class PremiumCustomer extends Customer {
taxRate = () => 0.05
discountRate = () => 0.1
}`
توجه داشته باشید که من از روش الگوی الگو استفاده کردم 🙂
و در اینجا گزینه دیگری با استفاده از FP وجود دارد
` مالیات بر هزینه = نرخ مالیات => قیمت => قیمت * نرخ مالیات
const discount = discountRate => price => price * discountRate
const regularTaxAmout = tax(0.1)
const premiumTaxAmout = tax(0.05)
const regularDiscount = discount(0)
const premiumDiscount = discount(0.1)
const calculationWithRates = (taxRateFunc, discountRateFunc) => price =>
price + taxRateFunc(price) - discountRateFunc(price)
const totalForRegularCustomer = calculationWithRates(regularTaxAmout, regularDiscount)
const totalForPremiumCustomer = calculationWithRates(premiumTaxAmout, premiumDiscount)`
توجه داشته باشید که من توابع را برای ارائه انتزاعات بیشتر و افزایش قابلیت اعلان انجام می دهم
باز هم، ما می توانیم هر دو پارادایم را در یک طرح ترکیبی ترکیب کنیم 🙂
` مشتری کلاس {
سازنده (نرخ مالیات، نرخ تخفیف) {
this.taxRate = taxRate
this.discountRate = discountRate
}
totalPrice = price => price + this.calculateTax(price) - this.calculateDiscount(price)
calculateTax = price => price * this.taxRate
calculateDiscount = price => price * this.discountRate
}
const regularCustomer = new Customer(0.1, 0)
const premiumCustomer = new Customer(0.05, 0.1)
const total = customer => customer.totalPrice
const totalForRegularCustomer = total(regularCustomer)
const totalForPremiumCustomer = total(premiumCustomer)`
روش/عملکردهای طولانی بیش از حد
کلاسیک دیگری که من به دیدن آن عادت دارم روش ها و عملکردهایی با مسئولیت های زیاد، بیش از حد طولانی، با قراردادهای سخت کدگذاری شده و قوانین تجاری پیچیده است.
` const tokenize = کد => {
let tokens = []اجازه دهید currentToken = ”
بگذارید currentType = null
for (let i = 0; i < code.length; i++) {
let char = code[i]
if (char === ' ' || char === '
‘ || کاراکتر === ”) {
if (currentToken !== ”) {
tokens.push({ type: currentType، مقدار: currentToken })
CurrentToken = ”
CurrentType = null
}
} else if (char === ‘+’ || char === ‘-‘ || char === ‘*’ || char === “https://dev.to/”) {
if (currentToken !== ”) {
tokens.push({ type: currentType، مقدار: currentToken })
currentToken = ”
}
tokens.push({ type: ‘operator’, value: char });
} else if (char >= ‘0’ && char <= '9') {
if (currentType !== ‘number’ && currentToken !== ”) {
tokens.push({ type: currentType، مقدار: currentToken })
CurrentToken = ”
}
CurrentType = “شماره”
CurrentToken += char
} else if ((char >= ‘a’ && char <= 'z') || (char >= ‘A’ && char <= 'Z')) {
if (currentType !== ‘شناسه’ && currentToken !== ”) {
tokens.push({ type: currentType، مقدار: currentToken })
CurrentToken = ”
}
CurrentType = شناسه
CurrentToken += char
}
}
if (currentToken !== '') {
tokens.push({ type: currentType, value: currentToken })
}
return tokens
}`
خیلی پیچیده است، درست است؟ فقط با خواندن کد نمی توان فهمید
بیایید اجرای مجدد این را با استفاده از OOP ببینیم که یک انتزاع ایجاد می کند و تغییرپذیری زمینه را محصور می کند.
Const binaryOperators = [‘+’, ‘-‘, ‘*’, “https://dev.to/”]
const SPECIAL_CHARS = {
EMPTY_STRING: '',
...
}
class Context {
constructor(code) {
this.code = code
this.tokens = []
this.currentToken = ''
this.currentType = null
}
...
}
class Tokenizer {
constructor(code) { this.context = new Context(code) }
getCurrentToken() { this.context.getCurrentToken() }
tokenize() {
this.context.getCode().forEach(this.processChar)
this.finalizeToken()
return this.context.getTokens()
}
processChar(char) {
if (this.isWhitespace(char)) {
this.finalizeToken()
} else {
...
}
}
isEmptyChar() {
return this.getCurrentToken() == SPECIAL_CHARS.EMPTY_STRING
}
finalizeToken() {
if (!isEmptyChar()) {
this.addToken(this.context.currentType(), this.currentToken())
this.resetCurrentToken()
}
}
addToken(type, value) {
this.context.addToken({ type, value })
}
resetCurrentToken() {
this.context.resetCurrentToken()
}
processDigit(char) {
if (!this.context.currentIsNumber()) {
this.finalizeToken();
this.context.setNumber()
}
this.context.nextChar()
}
processLetter(char) {
...
}
isWhitespace = char => char === SPECIAL_CHARS.SPACE || char === SPECIAL_CHARS.JUMP || char === SPECIAL_CHARS.TAB
isOperator = binaryOperators.includes
...
}`
یا می توانیم با استفاده از برخی دیگر از مفاهیم FP راه حلی بیندیشیم
` const isWhitespace = [‘ ‘, ‘
‘, ‘ ‘].شامل می شود
const isOperator = [‘+’, ‘-‘, ‘*’, “https://dev.to/”].شامل می شود
…
const createToken = (type, value) => ({ type, value })
const initialState = () => ({ tokens: [], currentToken: EMPTY_STRING, currentType: null })
const processChar = (state, char) => {
if (isWhitespace(char)) return finalizeToken(state)
if (isOperator(char)) return processOperator(state, char)
if (isDigit(char)) return processDigit(state, char)
if (isLetter(char)) return processLetter(state, char)
return state
}
const finalizeToken = state => isEmptyString(currentToken) ? state : ({
...state,
tokens: [ ...state.tokens, createToken(state.currentType, state.currentToken) ],
currentToken: '',
currentType: null
})
const processOperator = (state, char) => ({
...finalizeToken(state),
tokens: [...state.tokens, createToken(TYPES.op, char)]
})
const processDigit = (state, char) => ({
...state,
currentType: TYPES.Number,
currentToken: isNumber(state.currentType) ? state.currentToken + char : char
})
...
const tokenize = code => finalizeToken(code.reduce(processChar, initialState())).tokens
باز هم، ما در کپسولاسیون، انتزاعات و بیانی بودن فکر می کنیم
و البته، ما می توانیم یک راه حل ترکیبی ارائه دهیم 😉
const SPECIAL_CHARS = {
SPACE: ' ',
NEWLINE: '
‘،
TAB: “”
}
const binaryOperation = ['+', '-', '*', "https://dev.to/"]
class Tokenizer {
constructor(code) {
this.code = code
this.tokens = []
this.currentToken = EMPTY_STRING
this.currentType = null
}
tokenize() {
this.code.split('').forEach(this.processChar.bind(this))
this.finalizeToken()
return this.tokens
}
processChar = char => {
const processors = [
{ predicate: this.isWhitespace, action: this.handleWhitespace },
{ predicate: this.isOperator, action: this.handleOperator },
{ predicate: this.isDigit, action: this.handleDigit },
{ predicate: this.isLetter, action: this.handleLetter }
]
const { action } = processors.find(({ predicate }) => predicate(char)) || EMPTY_OBJECT
action?.(this, char)
}
isWhitespace = Object.values(SPECIAL_CHARS).includes
isOperator = binaryOperation.includes
isDigit = /[0-9]/.test
isLetter = /[a-zA-Z]/.test
handleWhitespace = this.finalizeToken
handleOperator = char => {
this.finalizeToken()
this.addToken(TYPES.operation, char)
}
handleDigit = char => {
if (!this.isNumber()) { this.finalizeToken(); this.currentType = TYPES.number }
this.currentToken += char
}
handleLetter = char => {
if (!this.isIdentifier()) { this.finalizeToken(); this.currentType = TYPES.id }
this.currentToken += char
}
finalizeToken() {
if (!this.isEmpty()) {
this.addToken(this.currentType, this.currentToken)
this.currentToken = SPECIAL_CHARS.empty
this.currentType = null
}
}
addToken(type, value) {
this.tokens.push({ type, value })
}
}
const tokenize = code => new Tokenizer(code).tokenize()
`
اجازه دهید این اولین مقاله را در مورد طراحی نرم افزار OOP + FP به پایان برسانیم، من می خواهم در مورد این دیدگاه بیشتر بنویسم و نمونه ها و الگوهای بسیار پیچیده تر را مرور می کنم، راه حل های ذوب شده را پیشنهاد می کنم و ایده های هر دو پارادایم را عمیقا بررسی می کنم.
اگر چیزی عجیب است، یا اگر چیزی در این مقاله پیدا کردید که میتوان آن را بهبود بخشید، به من اطلاع دهید. مانند نرم افزار ما، این یک فرآیند تکراری افزایشی است که با گذشت زمان بهبود می یابد.
با تشکر فراوان برای خواندن!