<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های حمیدرضا علیاری</title>
        <link>https://virgool.io/feed/@HamidrezaAliyari</link>
        <description>Software engineer</description>
        <language>fa</language>
        <pubDate>2026-06-16 23:12:39</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/406771/avatar/ZxOkE6.jpeg?height=120&amp;width=120</url>
            <title>حمیدرضا علیاری</title>
            <link>https://virgool.io/@HamidrezaAliyari</link>
        </image>

                    <item>
                <title>مقایسه عملی Covariance و Contravariance در سی‌شارپ</title>
                <link>https://virgool.io/@HamidrezaAliyari/%D9%85%D9%82%D8%A7%DB%8C%D8%B3%D9%87-%D8%B9%D9%85%D9%84%DB%8C-covariance-%D9%88-contravariance-%D8%AF%D8%B1-%D8%B3%DB%8C-%D8%B4%D8%A7%D8%B1%D9%BE-jzehggfolry9</link>
                <description>احتمالا زمانی که میخواهید یک اینترفیس از نوع Generic ایجاد کنید با پرسشی از طرف ریشارپر مواجه شدید که میگه پارامتر T میتونه به صورت Covariance یا Contravariance تعریف بشه.اگه تایید بزنید، به پارامتر T پیشوند in یا out اضافه میکنه.توی این مقاله میخوایم بررسی کنیم که تایپ‌های Covariant و Contravariant چیا هستن و به چه دردی میخورن و اینکه in و out به چه کاری میان.قصد داریم با مثال معروف شاگرد و استاد، این دو تا مفهوم رو مقایسه کنیم. پس بصورت زیر این کلاس هارو پیاده‌سازی میکنیم:public class Person {
    public Person(string name)
    {
        Name = name;
    }
    public string Name { get;}
}

public class Student : Person {
    public Student(string name) : base(name)
    {
    }
    // Student specific fields...
}

public class Teacher : Person {
    public Teacher(string name) : base(name)
    {
    }
    // Teacher specific fields...
}بدیهیه که کلاس Person به عنوان base قرار گرفته که فیلد Name داره و کلاس‌های Student و Teacher از این کلاس ارث‌بری میکنن.نکات:با توجه به اینکه کلاس‌های Student و Teacher از کلاس Person ارث‌بری میکنن، هر جایی که انتظار کلاس Person رو داریم، میتونیم کلاس‌های Teacher و Student رو جایگزینش کنیم.بطور مثال اگه یه متد ورودی‌ای از جنس Person داشته باشه، همواره میتونیم به جاش instance کلاس‌های Student و Teacher رو پاس بدیم.همچنین اگه یک متد طوری تعریف شده که کلاس Person به عنوان return type باشه، میتونیم کلاس‌های Student و Teacher رو هم return کنیم.این موارد برای کسایی که OOP کار میکنن کاملا بدیهیه و ابهامی نداره.اما سوالی که در اینجا مطرح میشه اینه که اگه یه متدی به عنوان پارامتر ورودیش، Person نه، بلکه یک Generic Type بصورت &lt;G&lt;Person بگیره چطور؟ ایا این به معنی اینه که میتونیم &lt;G&lt;Teacher یا &lt;G&lt;Student رو هم بهش پاس بدیم؟این دقیقا همین موردیه که میخوایم توی این مقاله بررسی کنیم. در انتهای این مقاله میتونیم پاسخ درستی به این سوال بدیم.بررسی Covariance در آرایه‌هابه متد زیر توجه کنید:public void PrintNames(Person[] people) {
    foreach (var person in people)
    {
        Console.WriteLine(person.Name);
    }
}این متد آرایه‌ای از Person رو توی ورودی میگیره و با دستور foreach اسم تک تک افراد رو توی console نمایش میده.اما اگه به جای آرایه‌ای از Person، ما آرایه‌ای از Student یا Teacher رو براش ارسال کنیم چطور؟ مثل زیر:Student[] students = 
{
    new Student(&amp;quotJohn&amp;quot), 
    new Student(&amp;quotPeter&amp;quot)
};
PrintNames(students);این کد به درستی compile و اجرا میشه بدون هیچ مشکلی، چون هر دو کلاس Teacher و Student شامل فیلد Name هستند که اجازه پرینت کردن رو به این متد میده. این باعث میشه ما بتونیم کارای بیشتری با این متد بکنیم در مقایسه با اینکه فقط بتونیم از یک تایپ خاص مثل Person به عنوان ورودیش استفاده کنیم. برای درک بهتر یه مثال دیگه رو بررسی کنیم:public void Update(Person[] people)
{
    people[0] = new Teacher(&amp;quotPaul&amp;quot);
}حالا اگه این متد رو به صورت زیر فراخوانی کنیم چه اتفاقی میافته؟Student[] students =
{
    new Student(&amp;quotJohn&amp;quot), 
    new Student(&amp;quotPeter&amp;quot)
};
Update(students);
Student firstStudent = students[0];این کد compile میشه ولی توی runtime بهمون ارور میده. یکم ساده‌ترش میکنیم:Student[] students =
{
    new Student(&amp;quotJohn&amp;quot), 
    new Student(&amp;quotPeter&amp;quot)
};
Person[] people = students;              //Line 6
people[0] = new Teacher(&amp;quotPaul&amp;quot);    // Line 7
Student student = students[0];        // Line 8این موارد رو بدقت دنبال کنید:جنس اولیه آرایه از نوع []Student است که با توجه به مواردی که بحث شد، میتونیم []Student رو جایگزین []Person بکنیم. توی خط ششم ما رفرنس زدیم روی []Person. همینطور هر Teacher ای یک نوع Person است که توی خط هفتم این مورد جلوی compile رو نمیگیره و ارور نمیده. اما این دقیقا اونجاییه که ما ارور زیر رو دریافت میکنیم:Attempted to access an element as a type incompatible with the array.البته انتظار این ارور رو میکشیدیم. اگه کد کار میکرد،چه اتفاقی توی خط هشتم میافتاد؟ ما اینجا سعی کردیم کلاس Teacher رو به Student اختصاص بدیم. این دو تایپ با همدیگه compatible نیستند و بی معنیه.اینکه ما میتونیم جایی که انتظار داریم []Person پاس داده بشه، به جاش []Student یا []Teacher پاس بدیم به این معنیه که این آرایه‌ها Covariant هستند.قبل از سی‌شارپ نسخه ۴ تمام generic type ها از نوع invariant بودن پس ما فقط میتونستیم دقیقا همون تایپ مشخص رو باهاش کار کنیم. طراحان زبان تصمیم گرفتن همین پیاده‌سازی که توی آرایه‌ها داریم رو هم برای generic typeها اضافه کنن تا بتونیم راحت‌تر از اونها استفاده کنیم.بررسی Covariance در Generic Type هاما مواردی رو بررسی کردیم که مشخص میکرد با استفاده از covariance میتونیم یکسری الگوریتم‌های مشترک رو بصورت یکجا استفاده کنیم. اما مثالی رو هم دیدیم که covariance در آرایه‌ها مارو با ارور مواجه کرد.میخوایم اینجا یک نگاه دقیقتری به مثال بالا داشته باشیم. مجددا بصورت زیر مینویسیم:public void PrintNames(Person[] people)
{
    foreach (var person in people)
    {
        Console.WriteLine(person.Name);
    }
}
public void Update(Person[] people)
{
    people[0] = new Teacher(&amp;quotPaul&amp;quot);
}همونطور که مشخصه، متد PrintNames المنت‌هایی از آرایه رو فقط read میکنه. اما از اونطرف، متد update، آرایه رو تغییر میده که دقیقا همین حرکت باعث بروز ارور و مشکل میشه. با آرایه‌های Covariant عملا هیچ تضمینی برای جنس پارامتر پاس داده شده نداریم و میتونه Person، Teacher یا Student باشه. پس ما نمیتونیم به راحتی عملیات update رو انجام بدیم.توی مثال ما، اگه پارامتر پاس داده شده از جنس []Teacher یا []Person باشه بطور صحیح عملیات انجام میشه اما اگه از نوع []Student باشه به ارور میخوره.این کد خیلی شکننده‌ست (fragile) و قانون سوم SOLID رو نقض میکنه. به همین دلیله که طراحان سی‌شارپ در ابتدا از پیاده‌سازی این موارد توی generic ها صرف نظر کردن.اما خب یه جاهایی هم covariance به کارمون میاد. اونجا چطور؟مثلا ما اگه اینترفیس generic مون فقط عملیات read برای پارامتر ورودیش انجام بده، میتونیم بگیم که ارورهای دریافتیمون تقریبا از بین میره. به همین دلیل توی سی‌شارپ ۴ تصمیم گرفته شد با علم به این موضوع، مباحث Covariance و Contravariance با استفاده از سینتکس out و in پیاده‌سازی بشه.بحث Contravariance بعدا توی همین مقاله بررسی خواهد شد.خب حالا بریم یه درک کلی در مورد اینکه یک اینترفیس generic بتونه پارامتری بصورت read-only داشته باشه رو بدست بیاریم.تصور کنید ک پارامتر T به عنوان مقدار برگشتی متد زیر در نظر گرفته میشه:public interface IMyReadOnlyCollection&lt;T&gt;
{
    T GetElementAt(int index);
}این مثال خوبی برای حالتیه که بخواهیم یک تایپی رو Covariant کنیم. که به سادگی و با اضافه کردن عبارت out به عنوان پیشوند برای T میتونیم بهش برسیم:public interface IMyReadOnlyCollection&lt;out T&gt;در اینجا به پارامتر T گفته میشه که توی این اینترفیس به عنوان output در نظر گرفته میشه.بعد از اعمال این تغییر، میتونیم به راحتی &lt;IMyReadOnlyCollection&lt;Personرو با &lt;IMyReadOnlyCollection&lt;Student یا &lt;IMyReadOnlyCollection&lt;Teacherجایگزین کنیم.اگه دقت کرده باشید، قبلا هم با یک سری اینترفیس‌های generic از نوع read-only کار کردید. دیگه جای تعجبی نداره که بدونید IEnumerator هم یک نوع از اوناست:public interface IEnumerator&lt;out T&gt; : IEnumerator, IDisposable
{
  T Current { get; }
}که به عنوان covariant شناخته میشود.همینطور IEnumerable که یک instance از نوع IEnumerator برمیگردونه هم Covariant است:public interface IEnumerable&lt;out T&gt; : IEnumerable
{
  IEnumerator&lt;T&gt; GetEnumerator();
}در ادامه به بررسی Contravariance میپردازیم.بررسی Contravariance در Generic Type هاتوی بعضی از مقاله‌ها، Contravariance رو برعکس Covariance تعریف میکنن. توضیح و تعریف درستی بنظر میاد ولی خیلی گنگه.اینجا میخوایم یکم شفاف‌تر بهش بپردازیم تا با مثال کاربردی، بتونیم بهتر درکش کنیم.خب اینجا یک اینترفیس ساده داریم برای مقایسه دو object:public interface IMyComparer&lt;T&gt;
{
    int Compare(T x, T y);
}و یک پیاده سازی برای Person:public class PersonComparer : IMyComparer&lt;Person&gt;
{
    public int Compare(Person x, Person y)
    {
        return string.CompareOrdinal(x.Name, y.Name);
    }
}این متد فقط فیلد Name این دوتا object رو مقایسه میکنه و تا همینجا کافیه تا بتونیم ساده‌تر پیش بریم.حالا این متد رو در نظر بگیرید:public int Compare(IMyComparer&lt;Student&gt; comparer)
{
    var s1 = new Student(&amp;quotJohn&amp;quot);
    var s2 = new Student(&amp;quotPeter&amp;quot);
    return comparer.Compare(s1, s2);
}این متد دوتا Student رو ایجاد میکنه و توسط comparer ای که از پارامترهاش میگیره که از جنس &lt;IMyComparer&lt;Student هستش، مقایسه رو انجام میده.ما قبلا یک پیاده‌سازی داشتیم که دوتا شی Person رو با name شون مقایسه میکنه توی کلاس PersonComparer. اینجا چه اتفاقی میافته اگه بخوایم از همون comparer برای مقایسه دوتا Student استفاده کنیم؟مثل اینجا:var personComparer = new PersonComparer();
var comparisonResult = Compare(personComparer);
Console.WriteLine(comparisonResult);اومدیم به عنوان پارامتر ورودی به متد Compare که &lt;IMyComparer&lt;Student انتظار داشت، به جاش PersonComparer پاس دادیم.این کد ارور زمان compile میده. قبل از سی‌شارپ ۴ هیچ کاری نمیتونستیم بکنیم. دلیل این ارور هم واضحه، چیزی که این متد انتظار داشته رو پاس ندادیم. اما منطقا اگه ما یک Comparer داریم که روی Person کار میکنه، باید بتونیم از اون برای مقایسه Student ها هم استفاده کنیم، چون Student یک نوع Person هست با ویژگی‌های اضافی‌تر. اگه ما میگیم که دو تا Person هم‌اسم، یکی هستند، باید بتونیم همین شرط رو هم توی Student داشته باشیم.این جاییه که Contravariance وارد بازی میشه. مشابه همون روشی که توی Covariance استفاده میکردیم، میتونیم اینجا هم پیش بریم. نیاز داریم که به پارامتر اینترفیس generic مون اینو بگیم. اما اینبار به جای استفاده از out، از in به عنوان پیشوند پارامترمون استفاده میکنیم.حالا اینترفیسمون به این شکل در میاد:public interface IMyComparer&lt;in T&gt;
{
    int Compare(T x, T y);
}حالا اینترفیس IMyComparer به عنوان Contravariance تعریف میشه. با این حرکت، میتونیم PersonComparer رو توی متد Compare که انتظار &lt;IMyComparer&lt;Student رو داشت، پاس بدیم.ارور compiler رفع شده و ما به چیزی که میخواستیم رسیدیدم که تونستیم از متد مشترکی برای مقایسه instance ها بهره ببریم.بررسی Invariance در Generic Type هابعضی موقعیت‌ها هست که ما نیاز داریم فقط همون تایپی که مشخص کردیم پاس داده بشه. این حالتیه که پارامتر generic ما، هم در موقعیت output و هم input قرار میگیره.همونطور که قبلا گفتیم، IEnumerable با توجه به پارامتر generic ای که بصورت read-only داره، به عنوان covariant شناخته میشد.اینجا مثال IList رو میزنیم:public interface IList&lt;T&gt; : ICollection&lt;T&gt;, IEnumerable&lt;T&gt;, IEnumerable
{
  T this[int index] { get; set; }
  int IndexOf(T item); 
  void Insert(int index, T item); 
  void RemoveAt(int index);
}ما نمیتونیم با این اینترفیس به صورت Covariant یا Contravariant رفتار کنیم. چون با توجه به پارامتر T که داره هم در موقعیت output قرار گرفته و هم input. اگر تلاش کنیم که اینکارو کنیم، به همون اروری که قبلا توی آرایه‌ها بهش برخورده بودیم، رو به رو میشیم.var students = new List&lt;Student&gt;();
IList&lt;Person&gt; people = students;
people.Add(new Teacher(&amp;quotPeter&amp;quot));
Student student = students[0];این کد ارور compile میده و به هیچ نحوی قابل رفع نیست مگر اینکه دقیقا همون تایپی که انتظار داره رو بهش پاس بدیم.خلاصهتوی این مقاله به بررسی مفاهیم Covariance در آرایه‌ها پرداختیم و در ادامه از ویژگی هایی که زبان در اختیارمون داده تا این مفاهیم رو توی Generic Type ها هم استفاده کنیم رو مشاهده کردیم.امیدوارم این مقاله براتون مفید بوده باشه.خوشحال میشم اگه سوالی داشتید، توی کامنت‌ها در موردش صحبت کنیم.نویسنده: حمیدرضا علیاریایمیل: hamidayr@gmail.com</description>
                <category>حمیدرضا علیاری</category>
                <author>حمیدرضا علیاری</author>
                <pubDate>Wed, 03 Nov 2021 18:25:20 +0330</pubDate>
            </item>
                    <item>
                <title>Orchestration Vs Choreography in Microservices</title>
                <link>https://virgool.io/@HamidrezaAliyari/orchestration-vs-choreography-in-microservices-gdl44impwmfj</link>
                <description>مبحثی که در این مقاله قصد داریم مورد بررسی قرار بدیم، مقایسه دو رویکرد ارتباطی (Integration) متفاوت میان سرویس‌‌های مختلف توی معماری ماکروسرویسه. یکی Orchestration و دیگری Choreography.هر زمان که شروع به مدلسازی بیزینس‌های پیچیده میکنیم، باید با چالش‌های ایجاد شده توسط این پیچیدگی، دست و پنجه نرم کنیم.با معماری ماکروسرویس، سریعتر به این چالش‌ها برمیخوریم.به عنوان مثال یک فروشگاه اینترنتی فروش آهنگ داریم. زمانی که یک نفر در فروشگاه ما ثبت نام میکند اتفاق‌های زیر باید رخ دهند:در نظر گرفتن اعتبار برای این فرد در سرویس حسابداری ارسال پکیج خوش‌آمد از طریق سیستم پستی‌ به مشتریارسال ایمیل خوش‌آمداگر بخواهیم این فرآیند را در یک فلوچارت نمایش دهیم کار بسیار راحتی داریم:فرآیند ایجاد مشتری جدید - شکل ۱اما زمانی که میخواهیم این فرآیند را بصورت عملی پیاده‌سازی کنیم‌؛ دو رویکرد مختلف معماری وجود دارد که میتوانیم استفاده کنیم،Orchestration و Choreography.با Orchestration میتوانیم وظیفه پیشبرد فرآیند را به یک سرویس مرکزی محول کنیم تا این سرویس، مراحل را به ترتیب و با صدا زدن سرویس‌های دیگر به پیش ببرد. اما در رویکرد Choreography، هر سرویس میداند در زمان وقوع یک اتفاق مشخص، چه وظیفه ای دارد و چه عملیاتی را باید انجام دهد و دیگر چیزی به نام سرویس مرکزی نداریم و هر سرویس بصورت مستقل کار میکند.حال اگر بخواهیم با روش Orchestration فرآیند را پیش ببریم، احتمالا ساده‌ترین کاری که میتوانیم انجام دهیم این است که سرویس Customer، وظیفه سرویس مرکزی را برعهده بگیرد. زمان ثبت نام و ایجاد مشتری، با سیستم حسابداری، پست و ایمیل از طریق Request/Response ارتباط برقرار کند. سرویس Customer خودش وظیفه رهگیری وضعیت مشتری در فرآیند را دارد. میتواند وضعیت ثبت مشتری در سیستم حسابداری، وضعیت ارسال ایمیل و اینکه بسته پستی Delivered شده است یا خیر را چک کند.فلوچارت این مدل بصورت زیر است و ما میتوانیم دقیقا به همین شکل در کد عمل کنیم.مدیریت فرآیند ایجاد مشتری با رویکرد Orchestration - شکل ۲معایب رویکرد Orchestration این است که سرویس Customer میتواند به عنوان اصلی ترین سرویس ما باشد و تمام فرآیندها و بیزینس‌ها از این سرویس شروع شوند که این احتمالا باعث میشود یک سرویس به عنوان &quot;god&quot; در سیستم ما وجود داشته باشد و به بقیه سرویس‌های Anemic، دستوراتی در سطح عملیات CRUD بدهد.اما با رویکرد Choreography میتوانیم به سرویس Customer فقط وظیفه ایجاد یک Event با عنوان مثلا CustomerCreated بدهیم. حالا سرویس‌های حسابداری، ارسال ایمیل و پست، به این Event که ارسال شده Subscribe میکنند و عملیات مورد نظر خود را انجام میدهند، مانند شکل پایین. این نوع رویکرد بصورت فوق‌العاده‌ای Decouple است. هر سرویس دیگری که هنگام ایجاد یک مشتری، نیاز داشته باشد عملیاتی را انجام دهد، فقط کافیست به CustomerCreated، گوش دهد و Subscribe کند تا بتواند زمانی که این Event از سرویس Customer ما Raise شد، عملیات مورد نظر خود را انجام دهد.مدیریت فرآیند ایجاد مشتری با رویکرد Choreography - شکل ۳این به این معنا است که در این رویکرد، کار بیشتری برای مانیتور کردن فرآیند داریم تا مطمئن شویم عملیات‌های صحیحی اتفاق افتاده است. برای مثال، ما چطور میتوانیم متوجه شویم که در سیستم حسابداری، باگی وجود داشته و حساب مشتری به درستی ایجاد نشده؟ میتوانیم برای مدیریت فرآیندمان ابزاری ایجاد کنیم که در صورت رخداد یک اتفاق غیر منتظره، Log بزند تا ما بتوانیم راحت تر باگ را شناسایی و رفع کنیم.بصورت کلی، به نظرمن سیستم‌هایی که بیشتر فرآیندها را بصورت Choreographed پیش میبرند، بیشتر Loosely Coupled هستند و برای اعمال تغییرات، انعظاف پذیری بیشتری دارند. اما این را در نظر بگیرید که برای مدیریت و مانیتور فرآیندها باید کار بیشتری انجام دهید.البته سیستم‌هایی وجود دارند که با اعمال Choreography بسیار شکننده شده‌اند، با CostOfChange های بسیار بالاتر. اما با این حال، ترجیح من به استفاده از رویکرد Choreography است، رویکردی که در آن هر قسمت از سیستم میداند چه کاری باید انجام دهد تا در نهایت فرآیند ما با موفقیت به پایان برسد.یک بحث دیگر هم هست که میتوانیم اینجا به آن بپردازیم.اینکه Call کردن سرویس‌های دیگر بصورت Synchronous ساده است و اگر Call های سریع و ساده‌ای داریم از آن استفاده کنیم. اما اگر بخواهیم با روش Request/Response پیش برویم و با فرآیند‌هایی که مدت زمان اجرای آن زیاد است مواجهیم، میتوانیم در اینجا بصورت Asynchronous انجام دهیم اما منتظر Callback آن بمانیم.از سویی دیگر ارتباط Asynchronous Event میتواند مارا به سمت پیاده‌سازی هر چه بهتر رویکرد Choreography سوق دهد که باعث توسعه سرویس‌های Decoupled میشود.نهایتا چیزی که ما میخواهیم به آن برسیم، اطمینان از آنکه بتوانیم سرویس‌هایمان را بصورت کاملا مستقل Deploy کنیم.چالش بعدی که با آن مواجه میشویم، نحوه پیاده‌سازی و ارتباط بصورت Choreography است که بحث آن از این مقاله خارج است و در آینده در یک مقاله دیگر به آن خواهیم پرداخت.برگرفته از کتاب  Building Microservices - Designing Fine Grained Systemsحمیدرضا علیاری</description>
                <category>حمیدرضا علیاری</category>
                <author>حمیدرضا علیاری</author>
                <pubDate>Thu, 20 May 2021 21:43:47 +0430</pubDate>
            </item>
            </channel>
</rss>