Node Master
1.02K subscribers
24 photos
2 files
156 links
Group Chat: @nodemastergp
Admin: @napoleon_n1
Download Telegram
امروز درمورد Duck Typing صحبت میکنیم. این موضوع مربوط به رفتار زبان های dynamic type میباشد. البته این تکنیک در زبان هایی static type هم در حالت های خاصی استفاده میشود. برای درک این موضوع با مثال در #Python شروع میکنیم.
class Duck:
def fly(self):
print("quak quak !")

class Airplane:
def fly(self):
print("boom boom !")

#Duck Test
def fly_test(vehicle):
vehicle.fly()

flyable = [Duck() , Airplane()]
for f in flyable:
fly_test(f)

یک جمله خیلی معروف و کلیدی در این مورد وجود داره که باتوجه به اون مثال بالا رو توضیح میدیم."if it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck."
به این معنی هست "اگر شبیه به اردک شنا میکنه کواک میکنه پس احتملا اردک هست."
در زبان های dynamic type مفهومی به اسم interface نداریم و اگر یک class یک method رو تغریف کرده باشه شما بدون نیاز به type میتونید اون method رو به راحتی call کنید. الان در کد بالا هیچ نشانه ای از type نیست و فانکشن fly_test بدون داشتن دانشی از این که ایا arg ورودی fly method دارد یا خیر این متد رو call میکنه. به این کار میگن duck testing. در کل بگم به هیچ عنوان fly_test براش مهم نیست vehicle چی هست فقط براش مهم هست که fly وجود داشته باشد.
حالا سوال پیش میاد که اگر این متد نبود چی؟ الان در این مثال اگر اگر متد fly نباشه روی یکی از این کلاس ها خب runtime error داریم و برنامه crash میکنه. دو دیدگاه برای جلوگیری از این مدل runtime error ها داریم که در پست بعدی برسی میکنیم هردو رو.
نکته جالب دیگه این هست که Airplane و Duck این دو کلاس هیچ رابطه ای با هم ندارند و صرفا فقط به صورت قراردادی هردو یک fly method رو implement کردن. در #TypeScript از Duck Testing زیاد استفاده میشه برای داشتن کد TypeSafe در حقیقت تکنیک Type Predict به نوعی Duck Test میتونه محسوب بشه.

این مقدمه ای برای این بحث بود. در حقیقت برای داشتن همچین مدل رفتاری مدل های فکری مختلفی در زبان های مختلف هست. در زبان #Java ما از inerface ها و abstract class ها استفاده میکنیم در #GoLang شما یک نوع دیگه از Duck Typing رو میبینید که بهش میگن Structural Typing و interface های golang بهمون کمک میکنن در #TypeScript هم Structural Typing به شدت مرسوم هست و مثال خواهیم زد، در پایتون که Duck Typing رو دیدید ولی نکته جالب اینجا هست که Structural Typing و abstract class ها هم در پایتون خیلی بهمون کمک میکنن ولی خب کمتر کسی از وجود این ها خبر داره.
این نکته هم بگم که هیچ کدوم از این راه حل ها نسبت به دیگری برتری خاصی ندارند صرفا نوع تفکره بیشتر البته البته زبان های Static و TypeSafe میتونن develop رو راحت تر کنن در این مورد. این که کدوم تکنیک در کدوم زبان استفاده میشه مهم هست چون که به عنوان مثال Duck Typing در java خیلی مرسوم نیست و استفاده ازش سخت تره اما در یک حالت های خاصی خیلی میتونه در این زبان کمک کنه.
#Tip
👍11
دوست دارم یکم درمورد Dependency injection صحبت کنم. یکی از پایه ای ترین Pattern ها که اوایل در زبان های Static Type خیلی محبوب شد ولی به زبان های Dynamic Type هم راه پیدا کرد. هر گوشه ای که در صنعت نگاه کنید این تکنیک رو میبینید که در حال استفاده است. از فریم ورک های محبوب مثل Spring boot در #Java و NestJS در #NodeJS و Angular در کد های #FrontEnd #JavaScript . حالا سوال پیش میاد چرا از این تکنیک استفاده میکنن؟
یک اتومبیل رو در نظر بگیرید از اجزای زیادی تشکیل شده است این اجزا به هم وابستگی های ( Dependency ) زیادی دارن اما نحوه قرار گرفتنشون در کنار هم خیلی جالبه. به این شکل که اگر قطعه ای خراب شد نیاز نیست کل ماشین رو بندازیم دور و به راحتی میتونیم قطعه مورد نظر رو جایگزین با قطعه جدید کنیم.

یک موضوع دیگ بنظرم یوتیوب برید ویدیو راجع به خط تولید ماشین ها نگاه کنید. میبینید که در استیج های مختلف با این که ماشین هنوز آماده نیست قطعات کوچک تر رو جدا تست میکنن ( Unit Test ) بعد ماشین یا که کامل شد کل ماشین رو یکجا تست میکنند ( End2End Test ). حالا این تکنیک اگر درست استفاده بشه فرایند تست نوشتن رو خیلی براتون راحت میکنه. دوستان تست نوشتن برای برنامه ای که مینویسید از خود برنامه سخت تره. یکی از محدود خط کش هایی که میتونیم باهاش معیار کد خوب و بد رو مشخص کنیم کدی هست که بشه براش تست نوشت و کدی که بشه low mock test انجام داد.

معمولا همه جا راجع به کد #SOLID صحبت میکنن اما این رو هیچ وقت نمیگن که اگر به دنبال #SOLID شدن حرکت میکنید بدون Dep Injection تقریبا غیرممکن هست. نکته جالب این که خیلی از تازه کار ها در مفاهیم Object Orinted مشکل دارند به طوری که درک مفهومی مثل Abstraction برای تازه کار ها یکم سخت هست. اما اگر به این Pattern در آموزش ها دقیق تر پرداخته میشد. بعد از یک مدت کار کردن به این موضوع میرسید که ۴ اصل Object Orinted خیلی به صورت طبیعی براتون منطقی میشه. مخصوصا تکنیک Abstraction که ابزار ما مثل آچار برای مکانیک ها هست که بهشون کمک میکنه قطعات یک اتومبیل رو عوض کنن ( ما هم با Abstraction میتونیم برنامه خودمون رو تبدیل به قطعه های کوچک تر کنیم و این قطعات رو در صورت نیاز تعویض کنیم )

صحبت کردن در مورد این مفاهیم یکم پیچیده هست به دلیل این که خیلی با مفاهیم دیگ مرتبط هستن. چند وقت پیش یک پست گذاشتم که گفتم اگر میخواید فقط یک چیز از این چنل یاد بگیرید این رو یاد بگیرید. اون پست اینجا خیلی مرتبط هست ولی خب به دلیل طولانی شدن کاری بهش نداریم فقط اشاره کردم خودتون به عنوان تمرین یکم فکر کنید روش.
https://t.iss.one/NodeMaster/137

از اونجایی که دوست دارم به جای ماهی دادن، ماهیگیری یاد بدم از اینجا به بعد درمورد این که چطور یک مفهومی که هیچی ازش نمیدونیم رو باهم یادبگیریم.
در کتاب های الگوریتم یکی از اولین الگوریتم هایی که درموردش صحبت میکنن Divide and conquer ( معادل فارسی تفرقه بینداز و حکومت کن ) هست. این الگوریتم محدود به کامپیوتر فقط نیست و میتونید در یادگیری خیلی استفاده کنید. و یک نمونه رو اینجا باهم میبینیم. این الگوریتم اینطور هست که اگر مسئله ای داریم بیایم و اون رو مسئله های کوچک تر تبدیل کنیم و حال کنیم.
خب به این شکل هست که برای درک بهتره Dependency injection بهتره ما اول یاد بگیرم خود Dependency چی هست. بعد injection رو یاد بگیریم. بعد فکر کنیم که ترکیب این دوتا چطور میشه Dependency injection حالا به جای یک مسئله بزرگ ۳ مسئله کوچک تر داریم با ترتیب میتونیم حل کنیم. خب تبریک میگم الگوریتم Divide and conquer هم یاد گرفتید.

بریم برای سوال اول. Dependency چیست ؟
function syncLogic1() {}
function syncLogic2() {}

function job() {
syncLogic1(); // dependency
// Some codes
syncLogic2(); // dependency
}
function main() {
job(); // dependency
}

main();

ما دوتا لاجیک داریم که باید به ترتیب انجام شوند به عنوان مثال و فانکشن job کاری هست که باید انجام بشه. وقتی ما از یک function یا class به صورت مستقیم در یک function یا class دیگ استفاده میکنیم در حقیقت داریم وابستگی ( Dependency ) شدیدی ایجاد میکنیم. الان فانکشن main به job وابستگی داره و job به syncLogic1 و syncLogic2. اگر بخوایم یک مثال قابل لمس تر بزنیم
import { randomInt } from "node:crypto";

class SendOTP {
sendSms() {
const number = randomInt(1000, 9999); // dependency
// Api call to send sms
}
}

کد بالا فرض کنید میخوایم کد otp ارسال کنیم با استفاده از randomInt یک کد میسازیم و چون مستقیم از این فانکشن استفاده کردیم در SendOTP class وابستگی ایجاد کردیم.

به دلیل طولانی شدن ادامه بحث رو در پست های بعد عمیق صحبت میکنیم.

#Tip پارت اول.
👍26
Node Master
دوست دارم یکم درمورد Dependency injection صحبت کنم. یکی از پایه ای ترین Pattern ها که اوایل در زبان های Static Type خیلی محبوب شد ولی به زبان های Dynamic Type هم راه پیدا کرد. هر گوشه ای که در صنعت نگاه کنید این تکنیک رو میبینید که در حال استفاده است. از فریم…
در پست قبل درمورد Dependency صحبت کردیم و یاد گرفتیم که Dependency چیست؟ سوال دومی که قرار بود درموردش صحبت کنیم این بود که Injection چیست؟ به مثال پایین دقت کنید.
import { randomInt } from "crypto";

// Normal Version
function generateOtp() {
const otp = randomInt(1000, 9999); // dependency
return otp;
}

// Injected Version
function generateOtp(fn) {
const otp = fn(1000, 9000);
return otp
}
const result = generateOtp(randomInt);

در ماشین ها یک قطعه داریم به اسم injector ( فارسی میگن انژکتور در حقیقت اینجکتور هست ). خب انجین نیاز به سوخت داره و این وسیله وظیفه inject کردن سوخت به انجین داره یعنی از این زاویه نگاه کنیم که انجین به سوخت وابستگی (Dependency) دارد و injector سوخت رو inject میکنه به انجین و حالا رسیدیم به خود Dependency Injection. میشه گفت این قطعه فریمورکی هست که کار Dependency Injection رو انجام میده برامون مثل #NestJS در #NodeJS و #Spring در #Java.
اینجا یک لاجیک ساده داریم که کد otp میسازیم. در نسخه اول خیلی ساده میایم فانکشن randomInt رو به عنوان Dependency در فانکشن generateOtp استفاده میکنیم. اما در نسخه دوم بجای این که مستقیم از randomInt استفاده کنیم اون رو از طریق argument به generateOtp پاس میدیم و از طریق reference که بهش داریم کد Otp رو generate میکنیم. در حقیقت ما فانکشن randomInt رو در فانکشن generateOtp با این کار که از طریق argument بهش inject ( تزریق ) کردیم.

پس اگر بخوایم جمع بندی کنیم که Injection چیست. یعنی یک object رو از طریق های مختلفی مثل argument ها در function پاس بدیم و مستقیم استفاده نکنیم.
حالا اگر تعریف بالا رو کامل تر کنیم به Dependency Injection میرسیم. به این صورت که بجای استفاده از مستقیم از Dependency اون رو به روش های مختلف مثل مثال دوم داخل object مورد نظر inject کنیم. یعنی در حقیقت generateOTP به randomInt وابستگی دارد و وقتی ما از طریق argument به randomInt رو inject میکنیم به generateOtp به این کار میگیم Dependency Injection.

حالا در برنامه نویسی OOP وقتی از class ها استفاده میکنیم. نکات دیگه ای هم هست که باید بهشون توجه کنیم
- singleton design pattern خیلی استفاده میشه و شما اگر از هر DI framework استفاده کنید روشی برای کنترل این موضوع به شما میده.
- فرایند Injection از طریق Constructor
- فرایند Injection از طریق setter method

اگر یکم بخوایم دقیق تر به DI نگاه کنیم در حقیقت DI یک نوع خیلی سفارشی سازی شده از IOC Pattern هست ( Inversion Of Control ). اگر یکم بخواید خودتون مطالعه کنید این اصطلاح DI/IOC رو خیلی کنار هم میشنوید. مثال هایی از IOC رو میتونید در سرتاسر اکو سیستم برنامه نویسی ببینید.

به صورت کلی در این پست با مفهوم DI به صورت خیلی ساده آشنا شدیم. پست بعدی به DI در برنامه نویسی Object Orinted نگاه میکنیم و درمورد نکات بالا مفصل حرف میزنیم و صد البته با Poor man di آشنا میشیم تا di container رو بتونیم بهتر یاد بگیریم
درمورد IOC هم در آینده به صورت کامل جدا صحبت میکنیم چون خیلی مهم هست و نباید به سادگی از کنارش عبور کنیم.

پارت دوم
#Tip
👍17
یکی از نقاط ضعفی که در #JavaScript وجود داره نبود استاندارد Error های مناسب طبق یک class hierarchy خوب مثل #Java یا #Python هست. حالا در #NodeJS سعی بر این شده که به این وضعیت رسیدگی بشه و وضعیت این Error ها به نسبت خود vanilla js خیلی بهتر هست.
در جهت همین موضوع یکی از Error های خیلی مهم در #NodeJS در حقیقت SystemError ها هستن. اگر دوست دارید درمورد سیستم عامل بیشتر بدونید این Error class شما رو یک خورده به سیستم عامل نزدیک تر میکنه. حالا سوال پیش میاد که SystemError چیست؟ اگر برنامه شما درخواستی داشته باشه از سیستم عامل ولی به هر دلیلی محدودیت هایی وجود داشته باشه #NodeJS با SystemError به شما اطلاع میده.
import fs from "node:fs/promises";
import util from "node:util";
try {
await fs.readFile("./invalid-file.txt");
} catch (err) {
console.log(err);
console.log(util.getSystemErrorName(err.errno));
}

به این کد دقت کنید. ما در اینجا سعی میکنیم که یک فایلی رو از روی دیسک بخونیم که اصلا این فایل وجود نداره. در حقیقت داریم از سیستم عامل درخواست open syscall داریم که فایل رو بخونه یا ایجاد کنه ( در صورت استفاده از flag مناسب برای syscall که اینجا ست نکردیم) با توجه به این مثال به به محدودیت از سمت سیستم عامل میخوریم. و حاصل این کد یک SystemError هست. حالا در "node:util" یک فانکشن کمکی وجود داره که درصورتی که کد SystemError رو که در err.errorno میتونید بهش دسترسی داشته باشید بهش بدید. اسم که در اینجا ENOENT به من میده. این SystemError ها چون مربوط به سیستم عامل هست به زبان برنامه نویسی خاصی وابستگی ندارند و در همه زبان ها شما میتونید اینها رو ببینید.
#Tip
👍13
یکی از نکات زیبا که درمورد #Deno و #JSR که وجود داره این هست که شما میتونید از پکیج های std دینو در #NodeJS استفاده کنید. همشون نه ولی به اکثرشون دسترسی دارید. به لینک زیر یک نگاهی بندازید و مشخصا میبینید که کدوم پکیج ها رو میتونید در پروژه #NodeJS خودتون استفاده کنید.
https://jsr.io/@std
به عنوان مثال پکیج @std/msgpack به پروژه خودمون اضافه میکنیم.
npx jsr add @std/msgpack

و بعد کد زیر رو با #NodeJS اجرا کنید.
import * as msgpack from "@std/msgpack";

const data = { name: "node-master" };

const enc = msgpack.encode(data);
const dec = msgpack.decode(enc);
console.log(dec);

حالا سوال پیش میاد که اصلا چرا همچین کاری باید کنیم. باتوجه به این که #JavaScript خودش std بزرگی مثل #Python یا #Java نداره گاهی اوقات نیاز به یک سری کدهای کمکی برای پروژه خودتون دارید و پکیج هایی مثل Lodash, es-toolkit کمک بزرگی کردن. اما حالا #Deno هم با این کار به اکوسیستم کمک بزرگی کرده و یک رنج از ابزار های کمکی که ممکنه در loadash و بقیه شبیهه بهش پیدا نکنید رو شاید اینجا پیدا کنید.

به عنوان مثال اگر پروژه کوچیکی دارید که فقط یک instance از server شما وجود داره استفاده از ابزاری مثل #Redis صرفا برای Cache زیاد منطقی نیست و خب در اکثرا اوقات یک Map ساده مشکل شما رو میتونه حل کنه. اگر ویژگی TTL هم بخواید داشته باشید خودتون میتونید پیاده سازی کنید با کمک Map ولی معمولا از پکیج node-cache در این سناریو استفاده میکنن. حالا یک گزینه دیگ داریم که مربوط به #Deno std هست.
https://jsr.io/@std/cache
این پکیج جدا از مثال بالا مدل های مختلف دیگ از cache رو در اختیار شما میزاره که نگاهی بندازید و جالبه.
👍5
Node Master
دلیل این که این کد جذابه این موضوع هست که بعضی قسمت های #NodeJS دسترسی هایی خیلی نزدیک به زبانی مثل C بهت میده. و اون Api نزدیک به Api های low level داخل C هست. نمونه دیگ از این موضوع میتونه وقتی باشه که یک فایل یا سوکت به صورت خام باز میکنید و یکدونه file…
syscall-nodemaster.png
36.2 KB
قبلا در این مثال اشاره کوچکی به File Descriptor ها باهم کردیم. امروز با هم یکم عمیق تر میشیم. قبل از هرچیزی بهتر است این موضوع را درک کنیم که وقتی از "File" در اینجا صحبت میکنیم منظور از فایل های ویدیویی و موسیقی و عکس و باینری به تنهایی نیست. در اینجا باید از زاویه دید سیستم عامل به کلمه "File" و معنی اون نگاه کنیم تا بتونیم درک مناسبی داشته باشیم. از زاویه دید OS هر File میتونه یک Directory یا نمونه های بالا که گفتم یا حتی انواع IO Device های مختلف مثل Mouse و Keyboard و البته socket رو نباید فراموش کرد.
شاید گیج شده باشید یا براتون سوال پیش اومده باشه که چرا باید یک File از زاویه دید OS معادل همچین چیز هایی باشه؟ جواب خیلی ساده هست داشتن یک Abstraction خوب با یک Interface یکسان برای کار کردن با انواع مختلف IO Device ها قدرت خیلی زیادی میده و البته توسعه رو خیلی آسون تر میکنه. البته ما به عنوان Backend developer مستقیما با این sys call ها کار نمیکنیم و زبان های برنامه نویسی سطح بلاتر این موضوع رو هنوز Abstract تر کردن و یک Interface ساده تری به ما دادن. درواقع این موضوع کار توسعه دهنده زبان های برنامه نویسی دیگ رو راحت تر کرده تا برای ما بتونن راحت تر یک Interface تمیز و خوب توسعه بدن.

حالا میرسیم به این که FD چیست؟
- یک عدد postive int کوچک میباشد که برای هر Process این عدد ها Unique هستن و Process ما با استفاده از این آیدی هایی که از Kernal اومده میتونه با IO Device های مختلف صحبت کنه. به عنوان مثال FD 123 در یک Process با یک سوکت اشاره داره و در یک Process دیگ به یک text file.

- به صورت کلی File Descriptor ها بخشی از POSIX Api هستن و سیستم عامل های UNIX-like دارن.
- روی ویندوز داستان ظاهرا متفاوت هست و یک مفهومی به اسم Handle دارن که جزیاتش رو زیاد نمیدونم و در آینده باهم یادمیگیرم.

اگر هنوز یکم درک موضوع Abstraction که بالاتر اشاره کردم براتون سخته. مثال جذاب زیر براتون روشن میکنه داستان رو.
package main

import (
"fmt"
"net"
"os"
)

const MY_TXT = "NodeMaster Is Alive!"
const WRITE_NET = "WRITE TO TCP SOCKET"
const WRITE_FILE = "WRITE TO FILE"

func main() {
fd, _ := os.OpenFile("myTest.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
nt, _ := net.Dial("tcp", "localhost:3000")
fmt.Println(WRITE_NET)
nt.Write([]byte(MY_TXT))
fmt.Println(WRITE_FILE)
fd.Write([]byte(MY_TXT))
fmt.Println(nt)
fmt.Println(fd)
}

اینجا یک برنامه #Golang خیلی ساده داریم که یک فایل معمولی باز میکنیم و یک tcp socket و MY_TXT رو داخل هردو این ها Write میکنیم و چندتا لاگ ساده داریم که کارمون راحت تر باشه برای پیدا کردن syscall ها برنامه رو build بگیرید و با command زیر برنامه رو اجرا کنید.
در حقیقت وقتی سوکت و یک فایل متنی روی دیسک باز میکنیم اون پشت syscall مربوطه به ما یک File Descriptor میده که میتونیم با اون کار هامون رو انجام بدیم.
strace ./main

این strace به ما کمک میکنه که syscall هایی که برنامه ما انجام میده رو trace کنیم. در عکس بالا ما چندین write syscall رو میبینیم که به عنوان arg اول یک File Descriptor میگیره و به عنوان arg دوم یک بافر دیتایی که ما میخوایم داخلش بنویسیم.
در شماره یک که ما عدد 6 رو میبینیم این 6 در حقیقت به یک tcp socket اشاره داره و در شماره دو داخل عکس عدد 3 به File descriptor مربوط به text file ما روی دیسک اشاره داره. همینطور که میبینید حتی داخل عکس هم بدون دونستن این موضوع اگر نگاه فقط به singture فاکنکشن write کنید از روی file descriptor ها به هیچ عنوان متوجه نمیشید که مقصدی که داخلش write میکنیم آیا یک socket هست یا یک فایل روی دیسک یا هر IO Device دیگه ای و دقیقا همین موضوع اون چیزی هست که ما میخوایم و میشه همون Abstraction که بالاتر درموردش صحبت کردیم.

یک نکته این که درسته در #Golang و #Java ما Writer و Reader اینترفیس داریم و حتی اگر سیستم عامل همچین قابلیتی به ما نمیداد دوباره همچین interface هایی وجود داشت ولی کار توسعه دهنده های این زبان ها بسیار سخت تر بود برای ایجاد این interface ها.
حالا دوتا نکته که دوست دارم داخل گروه نظرتون رو بگید و درموردشون صحبت کنیم.
1. داخل عکس بالا چندتا write دیگ هم داریم که عدد 1 رو به عنوان File descriptor استفاده کردن. حالا این عدد 1 چی هست؟ داخل گروه خوشحال میشم درموردش صحبت کنیم.
2. چرا از #Golang استفاده کردیم برای مثال؟

بعد از مدت ها این اولین پست من هست امیدوارم براتون مفید باشه و خیلی خیلی ممنونم که همیشه همراه من بودید و امیدوارم همگی موفق باشید رفقا.
👍14
درود و وقت بخیر دوستان. امیدوارم حالتون خوب باشه.
اخیر یک ویدیو دیدم درمورد این که چطور در Netflix از #Java استفاده میکنن. سوال پیش میاد چرا این ویدیو باید برای ما جالب باشه و اهمیت بدیم؟
باتوجه به این که این روزا به شدت تکنولوژی در حال پیشرفت هست و هر روز ابزارها و فریمورک های جدید رو شاهد هستیم. این حس رو به ما القا میکنه که باید سریع باشیم و این تکنولوژی های جدید رو یاد بگیریم و اگر یاد نگیریم عقب میافتیم و در بازار کار جایی نخواهیم داشت!

منطقی هست باتوجه به این سرعت پیشرفت و AI این حس FOMO خیلی بیشتر شده به خصوص برای دوستانی که تازه کارتر هستن و هنوز وارد بازار کار نشدند. این دسته افراد خیلی بیشتر آسیب میبینن چون تا حالا حقیقت ماجرا که اکثر شرکت ها به شدت نسبت به تغییر این دسته از تکنولوژی ها گارد دارن رو لمس نکردن و همین موضوع باعث ناراحتی و ترس خیلی زیادی میشه.

نکته جالبی که در این ویدیو میبینیم جدا از زاویه دید فنی. زاویه دید بیزینسی هم رو میبینیم که چطور تیم فنی #Netflix رو تحت تاثیر خودش گذاشته که به عنوان مثال درمورد مهاجرت کدبیس ها از #Java 8 به #Java 17 صحبت میکنه که در چندسال پیش انجام شده و این رو به عنوان یک نکته مهم بیان میکنه و درموردش و تاثیراتش صحبت میکنه. دوباره با این حال بعد از این همه تلاشی که کردن روی ورژن های مثل 21 یا 24 که جدیدتر هستن نیومدن و قطعا تا سال چند سال آینده روی همین java 17 خواهند موند.

نکته بعدی خود #Java هست؟ چرا باوجود چیزایی مثل #Golang #Rust از زبانی که برای boomer ها بوده استفاده میکنن؟
خب جواب این هست چرا اصلا استفاده نکنن؟ اکوسیستم خوبی داره سرعت بالایی داره و از همه مهم تر خیلی خیلی Stable هست و از نظر هزینه براشون به صرفه نیست که مهاجرت کنن روی تکنولوژی های دیگه.

این صحبت ها به این معنی نیست که #Java خوب هست یا #Golang یا بقیه زبان ها بد یا خوب هستند. هدف صحبت امروز بیشتر این هست که لزوما برای موفق بودن همیشه نیازی نیست آخرین فریمورک آخرین تکنولوژی Backend خفن رو بلد باشیم. هنوز هم کدبیس های #COBOL هست و #CLang و #C++ به این زودی ها جایی نمیرن و البته چقدر شرکت هایی وجود داره که روی Java 8 و .NET 3.5 هستن و البته #PHP هم هنوز نمرده و نشانه هایی از مردن هم نداره جز در ویدیو های Tech influencer ها.

https://www.youtube.com/watch?v=XpunFFS-n8I
👍18
همیشه ما همه تلاش داریم کد با Performance خوب توسعه بدیم بدون این که این موضوع رو تصور کنیم که داخل کدبیس های #JavaScript معمولا Performance شوخیه.
ولی امروز قراره درمورد یک ویژگی جدید که در آپدیت ES2025 به #JavaScript اضافه شده صحبت کنیم که بهمون کمک میکنه که Performance بهتری داشته باشیم. سمت #NodeJS در بیزینس لاجیک های پیچیده میتونه معجزه کنه. برای #FrontEnd هم کاربردی هست ولی باتوجه به این که مرورگر های قدیمی ساپورت نمیکنن خب قطعا به این زودی استفاده ازش رو نمیبینیم.
ویژگی جدید ما اضاف شدن یک static method جدید به Iterator هست.
Iterator.from()

حالا سوال پیش میاد که چطور این به ما کمک میکنه. فرض کنید یک array بزرگ دارید و میخواید data رو map کنید به یک شکل دیگه و برای این کار یک pipeline از map ها رو ایجاد کردید:
const data = [1, 2, 3, 4, 5];

const final = data
.map((item) => item.toString())
.map((item) => `- ${item} -`)
.map((item) => `${item} ${new Date()}`);

در نگاه اول مشکلی نداره ولی اگر با عینک Performance ببینیم دوتا مشکل میبینیم.
1. برای هر map مجبوریم یکبار کامل loop بزنیم خب 3 بار loop میزنیم پس داریم O(3n)
2. هربار که یک loop کامل میزنیم هربار داریم یک Array جدید بعد از map ایجاد میکنیم. به صورت خلاصه هر .map برابر هست با یک Array allocation جدید. خب اینجا یک array اورجینال داریم و 3 تا map پس 4 تا array allocation داریم.
ممکنه برای تازه کارترها سوال های زیر پیش بیاد:
1. خب چرا اصلا این استایل کد میزنیم؟
2. چرا همه رو داخل یک map انجام نمیدیم؟

پاسخ سوال اول:
- میتونیم با for ... of کار رو بهتر با یک loop در بیاریم ولی مسئله این هست که معمولا برنامه نویس های #JavaScript در اینجور مواقع حتی بدون این که خودشون بدونن دیدگاه Functional Programming دارن و خب از اونجایی که به صورت فلسفی FP ذات Declarative داره و به صورت فلسفی کار کردن با API ها Declarative خیلی راحت تر و لذت بخش تر از Imperative هست همچین چیزی رو میبینیم.
- اگر هم خیلی کنجکاوید بیشتر بدونید وقتش هست نگاهی به #Elixir #Scala یا حتی Lambda ها در #Java اونجا قشنگ متوجه میشید. یا اصلا مسیر رو برعکس برید و نگاهی به رویکرد #Golang کنید و فرق زمین تا آسمونی رو ببینید.
پاسخ سوال دوم:
- در بزینس لاجیک های پیچیده برای خوانایی کد داشتن map های بیشتر خیلی بهتر از این هست که یک map بزرگ داشته باشیم. منطقی هم هست چون خیلی بهمون God Object ها رو یادآوری میکنه.

خب حالا سوال پیش میاد چیکار کنیم؟ خیلی ساده هست کافیه فقط خط اول رو به این شکل عوض کنیم و array رو تبدیل کنیم به Iterator.
const data = [1, 2, 3, 4, 5, 6]; 
const data = Iterator.from([1, 2, 3, 4, 5, 6]);

خب دوباره الان سوال پیش میاد که WTF الان چی شد؟ سادس.
ما دیتا رو تبدیل کردیم به یک Iterator که ذات Iterator ها به صورت Lazy هست یعنی تا وقتی که نیاز به consume شدن data نباشه هیچ پردازشی انجام نمیشه و اگر هم نیاز به map کردن باشه دقیقا در runtime به صورت on-demand برای هر index تبدیل انجام میشه و ما نیازی به alloc کردن حافظه اضافه برای Array نداریم و هیچ loop اضافه ای هم درکار نیست.

حالا به این نکات توجه کنید:
- هر iterator رو فقط یکبار میشه consume کرد و اگر نیاز باشه باید دوباره ازش بسازی. در حقیقت با .toArray داریم consume کردن رو شبیه سازی میکنیم و دومی مقدار خالی به ما میده به خاطر iterator بودن.
const data = Iterator.from([1, 2, 3, 4, 5, 6]);

data.toArray()
data.toArray()

- در این قسمت به map ها باید توجه کرد که با هر بار call شدن یک Array جدید نمیسازن بلکه یک Iterator جدید که روی Iterator قبلی سوار هست رو به ما میده! پس در نتیجه با توجه به تعریف Iterator که بالاتر گفتم نه loop اضافه ای داریم و نه alloc اضافه.
const data = Iterator.from([1, 2, 3, 4, 5, 6]);

const final = data
.map((item) => item.toString())
.map((item) => `- ${item} -`)
.map((item) => `${item} ${new Date()}`);

حالا اگر یکم بیشتر دقت کنی میبینیم خیلی شبیه به stream ها هست. اصلا این دوتا api به شدت باهم سازگار هستن. در این حد که استریم ها رو میشه تبدیل کرد به iterator و برعکس. بقیه کد هم دقیقا به صورت مشابهه کار میکنه.
import { Readable } from "node:stream";
const data = Iterator.from([1, 2, 3, 4, 5, 6]);
const streamData = Readable.from(data);

دل نوشته:
حقیقتا دیگ نمیشه تفاوت بین stream, iterator, generator, rxjs, web stream, رو تشخیص داد😂. همشون رو میتونی جایگزین هم استفاده کنی. ( دلایل مختلفی برای وجود این همه api برای یک کار هست )
👍16