برنامه نویسی

طراحی نرم افزار با استفاده از 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 به پایان برسانیم، من می خواهم در مورد این دیدگاه بیشتر بنویسم و ​​نمونه ها و الگوهای بسیار پیچیده تر را مرور می کنم، راه حل های ذوب شده را پیشنهاد می کنم و ایده های هر دو پارادایم را عمیقا بررسی می کنم.

اگر چیزی عجیب است، یا اگر چیزی در این مقاله پیدا کردید که می‌توان آن را بهبود بخشید، به من اطلاع دهید. مانند نرم افزار ما، این یک فرآیند تکراری افزایشی است که با گذشت زمان بهبود می یابد.

با تشکر فراوان برای خواندن!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا