مطالب
Performance در AngularJS قدم پنجم
در این مقاله موضوعی را مطرح خواهم کرد که شاید برای خیلی‌ها این نوع کد نویسی خوشایند نباشد. حتی برای خود من هم خوشایند نیست؛ ولی نهایتا در بهبود Performance تاثیر خیلی زیادی دارد. به کد زیر دقت کنید.
<div ng-repeat="item in items">
     <div ng-if="setting.header">{{item.header}}</div>
     <div>{{item.title}}</div>
     <div ng-if="setting.footer">{{item.footer}}</div>
</div>
توضیح کد: فرض کنید سناریوی پروژه ما به این صورت هست که ما یک لیست داریم، شامل 3 فیلد که header، title و footer را در تنظیمات می‌توانیم مشخص کنیم که header و footer در شرایطی نمایش داده شود و در شرایطی نمایش داده نشود و یا حالت‌های دیگر. 

خوب مشکل چیست و راهکار چیست؟
فرض کنید لیست ما شامل 100 رکورد هست و در تنظیمات مشخص کرده‌ایم که header نمایش داده شود و footer نمایش داده نشود. اما اتفاقی بدی که میفتد این است که وقتی لیست در View ساخته می‌شود، 100 بار ng-if مربوط به header و footer چک میشود؛ در جمع 200 بار می‌شود. چه این مقادیر true باشند چه false فرقی نمی‌کند و 200 بار بررسی می‌شود.
راهکار این مشکل به این صورت است که ما باید از ng-if داخل ng-repeat استفاده نکنیم. اما برای پیاده سازی تنظیمات باید ng-if‌ها را قبل از ng-repeat بررسی کنیم. پس مسلما ng-repeatها باید قالب پیش بینی کرده ما را نسبت به ng-if‌ها درست کند. نتیجه‌ی کار به صورت کد زیر است که شاید برای شما هم خوشایند نباشد:
<div ng-if="setting.header && setting.footer">
     <div ng-repeat="item in items">
          <div>{{item.header}}</div>
          <div>{{item.title}}</div>
          <div>{{item.footer}}</div>
     </div>
</div>
<div ng-if="setting.header && setting.footer==false">
     <div ng-repeat="item in items">
          <div>{{item.header}}</div>
          <div>{{item.title}}</div>      
     </div>
</div>
<div ng-if="setting.header==false && setting.footer">
     <div ng-repeat="item in items">         
          <div>{{item.title}}</div>   
          <div>{{item.footer}}</div>   
     </div>
</div>
<div ng-if="setting.header==false && setting.footer==false">
     <div ng-repeat="item in items">         
          <div>{{item.title}}</div>                
     </div>
</div>
درست است من هم با شما موافق هستم که خوشایند نیست. در این کد ما همه‌ی حالت‌ها را پیش بینی و قالب مناسب هر شرط را درست کرده‌ایم. حجم کد چند برابر شده، ولی از لحاظ Performance در ساخت لیست در View در حد 98% بهبود پیدا کرده‌است. همان مثال قبلی را در نظر بگیرید. ng-if مربوط به header و footer در این کد فقط 4 بار بررسی می‌شود. چه 100 رکورد باشد، چه 1000 تا، چه 10 تا رکورد. 
در مورد ng-repeat‌ها هم نگران نباشید فقط یک بار اجرا میشوند. اگر کارکرد ng-if را در مقاله‌ی قبلی من ، خوانده باشید، متوجه‌ی این موضوع می‌شوید که element‌های داخلی و direction‌های AngularJS داخلی ng-if زمانی پردازش می‌شوند که شرط true باشد. از این روش زمانی استفاده کنید که تعداد داده‌ها و حالت‌های زیادی دارید و Performance اهمیت بیشتری دارد. امیدوارم مقاله‌ی مفیدی باشد.
اشتراک‌ها
دور دوم وبینار تست خودکار نرم افزار، از آغاز تا انجام - قسمت اول: Unit-Test

💡مجموعه وبینارهای «تست خودکار نرم افزار، از آغاز تا انجام» با این هدف ارایه می‌شوند تا موضوع تست خودکار نه تنها به عنوان یک مهارت بلکه به عنوان یک هنر، در تیم‌ها جدی گرفته شود. تست نوشتن با تستِ خوب نوشتن، متفاوت است. در قسمت اول به موضوع Unit Test پرداخته می‌شود. 

📌تاریخ برگزاری دور دوم این وبینار: جمعه، 11 خرداد ماه 1397، ساعت 15 

دور دوم وبینار تست خودکار نرم افزار، از آغاز تا انجام - قسمت اول: Unit-Test
اشتراک‌ها
وبینار تست خودکار نرم افزار، از آغاز تا انجام - قسمت اول: Unit-Test

💡مجموعه وبینارهای «تست خودکار نرم افزار، از آغاز تا انجام» با این هدف ارایه می‌شوند تا موضوع تست خودکار نه تنها به عنوان یک مهارت بلکه به عنوان یک هنر، در تیم‌ها جدی گرفته شود. تست نوشتن با تستِ خوب نوشتن، متفاوت است. در قسمت اول به موضوع Unit Test پرداخته می‌شود.

📌تاریخ برگزاری: جمعه، 21 اردیبهشت، ساعت 15

وبینار تست خودکار نرم افزار، از آغاز تا انجام - قسمت اول: Unit-Test
مطالب
PHP سریعتر از ASP.NET! افسانه یا واقعیت؟

چرا افسانه‌ای که می‌گوید PHP از ASP.NET سریعتر است اینقدر شایع است؟ در این مقاله به بیان حقایقی می‌پردازیم که این افسانه را زیر سوال می‌برد؟

خیلی وقتها در بسیاری از نوشته‌ها و اظهارنظر‌ها می‌بینیم ادعا می‌شود که PHP بسیار سریعتر از ASP.net است و اینکه ASP.net از لحاظ سرعت کند است. آزار دهنده‌ترین بخش این ادعاها، آن است که هر یک از آنها را که نگاه می‌کنی بصورت کاملا غیر واقع بینانه به موضوع نگاه می‌کنند و فقط بدون دلیل این موضوع را ادعا می‌کنند. زیرا به این موضوع بصورتی کاملا متعصبانه و بدور از واقعیتها نگاه می‌شود. به همین دلیل بصورت گسترده ای این افسانه در میان اهالی وب پذیرفته شده است.

حال بجای اینکه این موضوع را بارها و بارها در جاهای مختلف بیان کنیم، این مقاله را نوشته و در هر کجا که لازم باشد به آن ارجاع خواهیم داد. باید توجه کنید این حقیقت که زبان PHP یک زبان اصیل و قدرتمند است هیچ شکی در آن نیست اما اینکه بخواهیم بصورت مغرضانه و به این دلیل که ما از این زبان استفاده می‌کنیم، آنرا از هر لحاظ برتر از سایر زبانها بدانیم (کمی که نه) بسیار اغراق آمیز است.

این مقاله برای این نیست که ما هریک از این زبانها را زیر سوال ببریم. بلکه برای آن است که این موضوع را با دلایل منطقی و حقیقی بررسی کنیم که آیا اینکه می‌گویند PHP از ASP.net سریعتر است واقعیت دارد یا نه؟

Compiled در مقابل Interpreted Languages:

قبل از هرچیز ذکر این نکته الزامی است که این دو زبان تفاوتهای اساسی در base دارند. ASP.net یک زبان بهینه سازی و کامپایل شده است، به این معنی که کدهای نوشته شده در این زبان قبل از اینکه قابل اجرا شوند، به مجموعه ای از دستورالعمل‌های خاص ماشین تبدیل می‌شوند. از سوی دیگر PHP یک زبان تفسیر شده است، به این معنی که کدهای نوشته شده به همان شکل ذخیره شده و در زمان اجرا این کدها تفسیر می‌شوند. این موضوع بطور گسترده‌ای پذیرفته شده و ثابت شده است که برنامه‌های کامپایل شده به مراتب سریعتر از برنامه‌های تفسیر شده اجرا می‌شوند، به این دلیل که برنامه‌های تفسیر شده نیاز دارند تا در زمان اجرا به دستورالعملهای ماشین تبدیل شوند.

در اینجا به یک نقل قول از دانشنامه آزاد ویکی پدیا اشاره می‌کنم که میزان سریعتر بودن برنامه‌های کامپایل شده را نشان می‌دهد:

"A program translated by a compiler tends to be much faster than an interpreter executing the same program: even a 10:1 ratio is not uncommon. The mixed solution's efficiency is typically somewhere in between."

به این معنا که یک برنامه بصورت کامپایل شده بسیار سریعتر از همان برنامه بصورت تفسیر شده، اجرا می‌شود.

اعداد و ارقام:

حال که تئوری خود را مبنی بر دلیل سریعتر بودن ASP.net بیان کردیم بیایید با هم نگاهی به برخی آمارها بیاندازیم تا این تئوری را در عمل هم نشان داده باشیم.

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

نمودار اول: زمان صرف شده برای تولید ونمایش نتایج برای جستجوی وب سایت‌های کوچک


همانطور که میبینید زمان متوسط برای سایت PHP، 0.1500 ثانیه و برای سایت ASP.net، 0.0150 ثانیه است. یک تفاوت بزرگ: PHP ده برابر نسبت به ASP.net کندتر است!

نمودار دوم: زمان صرف شده برای تولید و نمایش نتایج برای جستجوی وب سایت‌های متوسط


PHP، 1.0097 ثانیه طول می‌کشد در حالی که ASP.net، 0.0810 ثانیه زمان نیاز دارد. می‌بینیم که PHP دوازده بار بیشتر از ASP.net زمان می‌برد.

درحال حاضر این آزمون با یک کد مشابه در زبانهای برنامه نویسی مختلف پیاده سازی و اجرا شد و نتیجه را مشاهده نمودید. حال این موضوع پیش میاید که این اجرای کدها بر روی سیستم عامل ویندوزی بوده است و این می‌تواند به نفع ASP.net باشد، پس همین آزمون را بر روی سیستم عامل لینوکسی مشاهده می‌کنیم.

آمارهای زیر از سایت معتبر shootout.alioth.debian.org گرفته شده است. این آمارها نحوه اجرای همان کد را بر روی سیستم عامل لینوکسی برای هردو زبان نشان می‌دهد: 

 

همانطور که مشاهده می‌کنید در سیستم لینوکسی نیز همچنان ASP.net سریعتر از PHP عمل می‌کند.

نتیجه گیری:

همین حالا جمله‌ی "asp.net vs php speed" را در google جستجو کنید. خواهید دید که در اکثر پست‌ها گفته شده که PHP از ASP.net سریعتر است اما دلیلی بر این ادعا نخواهید یافت و فقط در حد حرف است. مشکل این است که اکثر مردم وقتی چیزی را زیاد می‌بینند یا زیاد می‌شنوند بدون آنکه دلیل بخواهند آنرا می‌پذیریند و حتی بعضی اوقات از آن نیز دفاع می‌کنند که واقعا جای تاسف دارد.

توسعه وب بوسیله PHP کار خوبی است، بسیاری از اپلیکیشن‌ها و وبسایتهای شگفت انگیز توسط این زبان نوشته شده اند. اگر احساس می‌کنید PHP یک زبان برتر است از آن استفاده کنید اما این دلیل نمی‌شود که اطلاعات غلط را به دیگران القاء کنید و بدون دلیل و مدرک این زبان را از هر لحاظ برتر بدانید حال آنکه در این مقاله دیدیم که براساس چیزی که ارائه شد، ASP.net سرعت بیشتری نسبت به PHP دارد.

اگر با من در این امر موافق نیستید می‌توانید با نظرهای مستدل خود ما را راهنمایی کنید. 

نظرات مطالب
وی‍‍ژگی های پیشرفته ی AutoMapper - قسمت دوم
 آیا امکان تعریف AutoMapper در لایه‌های دیگر هم وجود دارد ؟ در صورت مثبت بودن پاسخ، چگونه اون رو توی لایه UI صدا بزنیم ؟
مطالب
درخت‌ها و گراف‌ها قسمت سوم
همانطور که در قسمت قبلی گفتیم، در این قسمت قرار است به پیاده سازی درخت جست و جوی دو دویی مرتب شده بپردازیم. در مطلب قبلی اشاره کردیم که ما متدهای افزودن، جستجو و حذف را قرار است به درخت اضافه کنیم و برای هر یک از این متدها توضیحاتی را ارائه خواهیم کرد. به این نکته دقت داشته باشید درختی که قصد پیاده سازی آن را داریم یک درخت متوازن نیست و ممکن است در بعضی شرایط کارآیی مطلوبی نداشته باشد.
همانند مثال‌ها و پیاده سازی‌های قبلی، دو کلاس داریم که یکی برای ساختار گره است <BinaryTreeNode<T و دیگری برای ساختار درخت اصلی <BinaryTree<T.
کلاس BinaryTreeNode که در پایین نوشته شده‌است بعدا داخل کلاس BinaryTree قرار خواهد گرفت:
internal class BinaryTreeNode<T> :
    IComparable<BinaryTreeNode<T>> where T : IComparable<T>
{
    // مقدار گره
    internal T value;
 
    // شامل گره پدر
    internal BinaryTreeNode<T> parent;
 
    // شامل گره سمت چپ
    internal BinaryTreeNode<T> leftChild;
 
    // شامل گره سمت راست
    internal BinaryTreeNode<T> rightChild;
 
    /// <summary>سازنده</summary>
    /// <param name="value">مقدار گره ریشه</param>
    public BinaryTreeNode(T value)
    {
        if (value == null)
        {
            // از آن جا که نال قابل مقایسه نیست اجازه افزودن را از آن سلب می‌کنیم
            throw new ArgumentNullException(
                "Cannot insert null value!");
        }
 
        this.value = value;
        this.parent = null;
        this.leftChild = null;
        this.rightChild = null;
    }
 
    public override string ToString()
    {
        return this.value.ToString();
    }
 
    public override int GetHashCode()
    {
        return this.value.GetHashCode();
    }
 
    public override bool Equals(object obj)
    {
        BinaryTreeNode<T> other = (BinaryTreeNode<T>)obj;
        return this.CompareTo(other) == 0;
    }
 
    public int CompareTo(BinaryTreeNode<T> other)
    {
        return this.value.CompareTo(other.value);
    }
}
تکلیف کدهای اولیه که کامنت دارند روشن است و قبلا چندین بار بررسی کردیم ولی کدها و متدهای جدیدتری نیز نوشته شده‌اند که آن‌ها را بررسی می‌کنیم:
ما در مورد این درخت می‌گوییم که همه چیز آن مرتب شده است و گره‌ها به ترتیب چیده شده اند و اینکار تنها با مقایسه کردن گره‌های درخت امکان پذیر است. این مقایسه برای برنامه نویسان از طریق یک ذخیره در یک ساختمان داده خاص یا اینکه آن را به یک نوع Type قابل مقایسه ارسال کنند امکان پذیر است. در سی شارپ نوع قابل مقایسه با کلمه‌های کلیدی زیر امکان پذیر است:
T : IComparable<T>
در اینجا T می‌تواند هر نوع داده‌ای مانند Byte و int و ... باشد؛ ولی علامت : این محدودیت را اعمال می‌کند که کلاس باید از اینترفیس IComparable ارث بری کرده باشد. این اینترفیس برای پیاده‌سازی تنها شامل تعریف یک متد است به نام (CompareTo(T obj که عمل مقایسه داخل آن انجام می‌گردد و در صورت بزرگ بودن شیء جاری از آرگومان داده شده، نتیجه‌ی برگردانده شده، مقداری مثبت، در حالت برابر بودن، مقدار 0 و کوچکتر بودن مقدارمنفی خواهد بود. شکل تعریف این اینترفیس تقریبا چنین چیزی باید باشد:
public interface IComparable<T>
{
    int CompareTo(T other);
}
نوشتن عبارت بالا در جلوی کلاس، به ما این اطمینان را می‌بخشد که که نوع یا کلاسی که به آن پاس می‌شود، یک نوع قابل مقایسه است و از طرف دیگر چون می‌خواهیم گره‌هایمان نوعی قابل مقایسه باشند <IComparable<T را هم برای آن ارث بری می‌کنیم.
همچنین چند متد دیگر را نیز override کرده‌ایم که اصلی‌ترین آن‌ها GetHashCode و Equal است. موقعی که متد CompareTo مقدار 0 بر می‌گرداند مقدار برگشتی Equals هم باید True باشد.
... و یک نکته مفید برای خاطرسپاری اینکه موقعیکه دو شیء با یکدیگر برابر باشند، کد هش تولید شده آن‌ها نیز با هم برابر هستند. به عبارتی اشیاء یکسان کد هش یکسانی دارند. این رفتار سبب می‌شود که که بتوانید مشکلات زیادی را که در رابطه با مقایسه کردن پیش می‌آید، حل نمایید. 

پیاده سازی کلاس اصلی BinarySearchTree
مهمترین نکته در کلاس زیر این مورد است که ما اصرار داشتیم، T باید از اینترفیس IComparable مشتق شده باشد. بر این حسب ما می‌توانیم با نوع داده‌هایی چون int یا string کار کنیم، چون قابل مقایسه هستند ولی نمی‌توانیم با  []int یا streamreader کار کنیم چرا که قابل مقایسه نیستند.
public class BinarySearchTree<T>    where T : IComparable<T>
{
    /// کلاسی که بالا تعریف کردیم
    internal class BinaryTreeNode<T> :
        IComparable<BinaryTreeNode<T>> where T : IComparable<T>
    {
        // …
    }
 
    /// <summary>
    /// ریشه درخت
    /// </summary>
    private BinaryTreeNode<T> root;
 
    /// <summary>
    /// سازنده کلاس
    /// </summary>
    public BinarySearchTree()
    {
        this.root = null;
    }
 
//پیاده سازی متدها مربوط به افزودن و حذف و جست و جو
}
در کد بالا ما کلاس اطلاعات گره را به کلاس اضافه می‌کنیم و یه سازنده و یک سری خصوصیت رابه آن اضافه کرده ایم.در این مرحله گام به گام هر یک از سه متد افزودن ، جست و جو و حذف را بررسی می‌کنیم و جزئیات آن را توضیح می‌دهیم.

افزودن یک عنصر جدید
افزودن یک عنصر جدید در این درخت مرتب شده، مشابه درخت‌های قبلی نیست و این افزودن باید طوری باشد که مرتب بودن درخت حفظ گردد. در این الگوریتم برای اضافه شدن عنصری جدید، دستور العمل چنین است: اگر درخت خالی بود عنصر را به عنوان ریشه اضافه کن؛ در غیر این صورت مراحل زیر را نجام بده:
  • اگر عنصر جدید کوچکتر از ریشه است، با یک تابع بازگشتی عنصر جدید را به زیر درخت چپ اضافه کن.
  • اگر عنصر جدید بزرگتر از ریشه است، با یک تابع بازگشتی عنصر جدید را به زیر درخت راست اضافه کن.
  • اگر عنصر جدید برابر ریشه هست، هیچ کاری نکن و خارج شو.

پیاده سازی الگوریتم بالا در کلاس اصلی:
public void Insert(T value)
{
    this.root = Insert(value, null, root);
}
 
/// <summary>
/// متدی برای افزودن عنصر به درخت
/// </summary>
/// <param name="value">مقدار جدید</param>
/// <param name="parentNode">والد گره جدید</param>
/// <param name="node">گره فعلی که همان ریشه است</param>
/// <returns>گره افزوده شده</returns>
private BinaryTreeNode<T> Insert(T value,
        BinaryTreeNode<T> parentNode, BinaryTreeNode<T> node)
{
    if (node == null)
    {
        node = new BinaryTreeNode<T>(value);
        node.parent = parentNode;
    }
    else
    {
        int compareTo = value.CompareTo(node.value);
        if (compareTo < 0)
        {
            node.leftChild =
                Insert(value, node, node.leftChild);
        }
        else if (compareTo > 0)
        {
            node.rightChild =
                Insert(value, node, node.rightChild);
        }
    }
 
    return node;
}
متد درج سه آرگومان دارد، یکی مقدار گره جدید است؛ دوم گره والد که با هر بار صدا زدن تابع بازگشتی، گره والد تغییر خواهد کرد و به گره‌های پایین‌تر خواهد رسید و سوم گره فعلی که با هر بار پاس شدن به تابع بازگشتی، گره ریشه‌ی آن زیر درخت است.
در مقاله قبلی اگر به یاد داشته باشید گفتیم که جستجو چگونه انجام می‌شود و برای نمونه به دنبال یک عنصر هم گشتیم و جستجوی یک عنصر در این درخت بسیار آسان است. ما این کد را بدون تابع بازگشتی و تنها با یک حلقه while پیاده خواهیم کرد. هر چند مشکلی با پیاده سازی آن به صورت بازگشتی وجود ندارد.
الگوریتم از ریشه بدین صورت آغاز می‌گردد و به ترتیب انجام می‌شود:
  • اگر عنصر جدید برابر با گره فعلی باشد، همان گره را بازگشت بده.
  • اگر عنصر جدید کوچکتر از گره فعلی است، گره سمت چپ را بردار و عملیات را از ابتدا آغاز کن (در کد زیر به ابتدای حلقه برو).
  • اگر عنصر جدید بزرگتر از گره فعلی است، گره سمت راست را بردار و عملیات را از ابتدا آغاز  کن.
در انتها اگر الگوریتم، گره را پیدا کند، گره پیدا شده را باز می‌گرداند؛ ولی اگر گره را پیدا نکند، یا درخت خالی باشد، مقدار برگشتی نال خواهد بود.

حذف یک عنصر
حذف کردن در این درخت نسبت به درخت دودودیی معمولی پیچیده‌تر است. اولین گام این عمل، جستجوی گره مدنظر است. وقتی گره‌ایی را مدنظر داشته باشیم، سه بررسی زیر انجام می‌گیرد:
  • اگر گره برگ هست و والد هیچ گره‌ای نیست، به راحتی گره مد نظر را حذف می‌کنیم و ارتباط گره والد با این گره را نال می‌کنیم.
  • اگر گره تنها یک فرزند دارد (هیچ فرقی نمی‌کند چپ یا راست) گره مدنظر حذف و فرزندش را جایگزینش می‌کنیم.
  • اگر گره دو فرزند دارد، کوچکترین گره در زیر درخت سمت راست را پیدا کرده و با گره مدنظر جابجا می‌کنیم. سپس یکی از دو عملیات بالا را روی گره انجام می‌دهیم.
اجازه دهید عملیات بالا را به طور عملی بررسی کنیم. در درخت زیر ما می‌خواهیم گره 11 را حذف کنیم. پس کوچکترین گره سمت راست، یعنی 13 را پیدا می‌کنیم و با گره 11 جابجا می‌کنیم.

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

/// عنصر مورد نظر را جست و جوی می‌کند و اگر مخالف نال بود گره برگشتی را به تابع حذف ارسال می‌کند
public void Remove(T value)
{
    BinaryTreeNode<T> nodeToDelete = Find(value);
    if (nodeToDelete != null)
    {
        Remove(nodeToDelete);
    }
}
 
private void Remove(BinaryTreeNode<T> node)
{
    //بررسی می‌کند که آیا دو فرزند دارد یا خیر
    // این خط باید اول همه باشد که مرحله یک و دو بعد از آن اجرا شود
    if (node.leftChild != null && node.rightChild != null)
    {
        BinaryTreeNode<T> replacement = node.rightChild;
        while (replacement.leftChild != null)
        {
            replacement = replacement.leftChild;
        }
        node.value = replacement.value;
        node = replacement;
    }
 
    // مرحله یک و دو اینجا بررسی میشه
    BinaryTreeNode<T> theChild = node.leftChild != null ?
            node.leftChild : node.rightChild;
 
    // اگر حداقل یک فرزند داشته باشد
    if (theChild != null)
    {
        theChild.parent = node.parent;
 
        // بررسی می‌کند گره ریشه است یا خیر
        if (node.parent == null)
        {
            root = theChild;
        }
        else
        {
            // جایگزینی عنصر با زیر درخت فرزندش
            if (node.parent.leftChild == node)
            {
                node.parent.leftChild = theChild;
            }
            else
            {
                node.parent.rightChild = theChild;
            }
        }
    }
    else
    {
        // کنترل وضعیت موقعی که عنصر ریشه است
        if (node.parent == null)
        {
            root = null;
        }
        else
        {
            // اگر گره برگ است آن را حذف کن
            if (node.parent.leftChild == node)
            {
                node.parent.leftChild = null;
            }
            else
            {
                node.parent.rightChild = null;
            }
        }
    }
}

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


دو فرزندی

اگر گره ما دو فرزند داشته باشد، گره سمت راست را گرفته و از آن گره آن قدر به سمت چپ حرکت می‌کنیم تا به برگ یا گره تک فرزنده که صد در صد فرزندش سمت راست است، برسیم و سپس این دو گره را با هم تعویض می‌کنیم.


تک فرزندی

در مرحله بعد بررسی می‌کنیم که آیا گره یک فرزند دارد یا خیر؛ شرط بدین صورت است که اگر فرزند چپ داشت آن را در theChild قرار می‌دهیم، در غیر این صورت فرزند راست را قرار می‌دهیم. در خط بعدی باید چک کرد که theChild نال است یا خیر. اگر نال باشد به این معنی است که غیر از فرزند چپ، حتی فرزند راست هم نداشته، پس گره، یک برگ است ولی اگر مخالف نال باشد پس حداقل یک گره داشته است.

اگر نتیجه نال نباشد باید این گره حذف و گره فرزند ارتباطش را با والد گره حذفی برقرار کند. در صورتیکه گره حذفی ریشه باشد و والدی نداشته باشد، این نکته باید رعایت شود که گره فرزند بری متغیر root که در سطح کلاس تعریف شده است، نیز قابل شناسایی باشد.

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


بدون فرزند (برگ)

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


پیمایش درخت به روش DFS یا LVR یا In-Order

public void PrintTreeDFS()
{
    PrintTreeDFS(this.root);
    Console.WriteLine();
}
 

private void PrintTreeDFS(BinaryTreeNode<T> node)
{
    if (node != null)
    {
        PrintTreeDFS(node.leftChild);
        Console.Write(node.value + " ");
        PrintTreeDFS(node.rightChild);
    }
}


در مقاله بعدی درخت دودویی متوازن را که پیچیده‌تر از این درخت است و از کارآیی بهتری برخوردار هست، بررسی می‌کنیم.

نظرات مطالب
الگوی طراحی Factory Method به همراه مثال
ممنونم بابت توضیحی که در مورد این الگو ارائه دادید و همچنین مثال خوبی که ارائه کردید، ولی چند تا سوال:
1- چرا کلاس VehicleFactory هم از نوع اینترفیس انتخاب نشده است؟ (آیا این موضوع سلیقه ای است؟)
2- استفاده از کلمه کلید string به جای نام کلاس String آیا تفاوتی در سرعت اجرا ایجاد میکند؟
3- چرا در دیاگرام uml رابطه بین ConcreteCreator و Concreteproduct از نوع dependency است و از نوع Association نیست؟ یعنی در مثال رابطه بین ConcreteVehicleFactory و یکی از کلاس‌های Bike و یا Scooter
4-چرا در ویژوال استودیو تولید خودکار uml از کد موجود متفاوت با دیاگرام فعلی است، مثلا نوع روابط درست نمایش داده نمیشه و همچنین رابطه ای که در مورد 3 اشاره شد در اینجا وجود نداره؟ آیا علتش نقص در این ابزار است؟ اگر بله، آیا ابزاری وجود داره که دیاگرام رو دقیقتر جنریت کنه؟
و یک نکته:
در کلاس‌های Scooter و Bike نیازی به استفاده از متد ToString‌برای تبدیل مقدار عددی miles نیست چون با یک عبارت رشته ای دیگه جمع شده به صورت درونی این متد توسط CLR فراخوانی میشه. البته این مورد رو Resharper دوست داشتنی تذکر داد. بهتره قبل از ارائه سورس پیشنهادات Resharpe هم روی کد اعمال بشه تا کد در بهترین وضعیت ارائه بشه.
ممنونم/.
نظرات مطالب
مدیریت سراسری خطاها در یک برنامه‌ی Angular
- در مورد اینکه چه استثناهایی باید مدیریت شوند یا خیر، مطلب «نکات کار با استثناءها در دات نت» را مطالعه کنید.
- علت عمل نکردن فیلتری که به آن لینک دادید (که من با آن موافق نیستم)، این است که دیگر نباید از میان‌افزار مدیریت استثناهای مخصوص توسعه دهنده‌های ASP.NET Core در این حالت استفاده کنید، چون با آن تداخل می‌کند و پیش از آن وارد عمل می‌شود. علت دریافت صفحه‌ی HTML ایی که مشاهده می‌کنید، همین مورد است. این صفحه برای برنامه‌های ASP.NET Core دارای Viewهای Razor طراحی شده‌است و نه مخصوص حالت کار صرفا Web API آن.
- یکی از مشکلات آن فیلتر هم این است که به هیچ عنوان نباید اصل خطای رخ‌داده‌ی در سمت سرور را به سمت کلاینت ارسال کرد و به کاربر نمایش داد. این مورد امکان دیباگ از راه دور برنامه‌ی شما را توسط یک مهاجم سهولت می‌بخشد و از دیدگاه امنیتی اشتباه است. این موارد را فقط باید توسط امکانات Logging توکار ASP.NET Core ثبت و در سمت سرور با «دسترسی ادمین» بررسی کنید. کاربر هم فقط باید جمله‌ی کلی «خطایی رخ داده‌است» را مشاهده کند و نه جزئیات آن‌را.