‏نمایش پست‌ها با برچسب Design patterns. نمایش همه پست‌ها
‏نمایش پست‌ها با برچسب Design patterns. نمایش همه پست‌ها

۱۳۹۰/۰۶/۰۴

كمپين ضد IF !


بكارگيري بيش از حد If و خصوصا Switch برخلاف اصول طراحي شيءگرا است؛ تا اين حد كه يك كمپين ضد IF هم وجود دارد!



البته سايت فوق بيشتر جنبه تبليغي براي سمينارهاي گروه مذكور را دارد تا اينكه جنبه‌ي آموزشي/خود آموزي داشته باشد.

يك مثال كاربردي:
فرض كنيد داريد يك سيستم گزارشگيري را طراحي مي‌كنيد. به جايي مي‌رسيد كه نياز است با Aggregate functions سروكار داشته باشيد؛ مثلا جمع مقادير يك ستون را نمايش دهيد يا معدل امتيازهاي نمايش داده شده را محاسبه كنيد و امثال آن. طراحي متداول آن به صورت زير خواهد بود:

using System.Collections.Generic;
using System.Linq;

namespace CircularDependencies
{
    public enum AggregateFunc
    {
        Sum,
        Avg
    }

    public class AggregateFuncCalculator
    {
        public decimal Calculate(IList<decimal> list, AggregateFunc func)
        {
            switch (func)
            {
                case AggregateFunc.Sum:
                    return getSum(list);
                case AggregateFunc.Avg:
                    return getAvg(list);
                default:
                    return 0m;
            }
        }

        private decimal getAvg(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum() / list.Count;
        }

        private decimal getSum(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum();
        }
    }
}

در كلاس AggregateFuncCalculator يك متد Calculate داريم كه توسط آن قرار است روي list دريافتي يك سري عمليات انجام شود. عمليات پشتيباني شده هم توسط يك enum معرفي شده؛ براي مثال اينجا فقط جمع و ميانگين پشتيباني مي‌شوند.
و مشكل طراحي اين كلاس، همان switch است كه برخلاف اصول طراحي شيء‌گرا مي‌باشد. يكي از اصول طراحي شيءگرا بر اين مبنا است كه:
يك كلاس بايد جهت تغيير، بسته اما جهت توسعه، باز باشد.

يعني چي؟
داستان طراحي Aggregate functions كه فقط به جمع و ميانگين خلاصه نمي‌شود. امروز مي‌گويند واريانس چطور؟ فردا خواهند گفت حداقل و حداكثر چطور؟ پس فردا ...
به عبارتي اين كلاس جهت تغيير بسته نيست و هر روز بايد بر اساس نيازهاي جديد دستكاري شود.

چكار بايد كرد؟
آيا مي‌توانيد در كلاس AggregateFuncCalculator يك الگوي تكراري را تشخيص دهيد؟ الگوي تكراري موجود، محاسبات بر روي يك ليست است. پس مي‌شود بر اساس آن يك اينترفيس عمومي را تعريف كرد:

public interface IAggregateFunc
{
     decimal Calculate(IList<decimal> list);
}

اكنون هر كدام از پياده سازي‌هاي موجود در كلاس AggregateFuncCalculator را به يك كلاس جدا منتقل خواهيم كرد تا يك اصل ديگر طراحي شيءگرا نيز محقق شود:
هر كلاس بايد تنها يك كار را انجام دهد.

public class Sum : IAggregateFunc
{
        public decimal Calculate(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum();
        }
}

public class Avg : IAggregateFunc
{
        public decimal Calculate(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum() / list.Count;
        }
}

تا اينجا 2 هدف مهم حاصل شده است:
- كم كم كلاس AggregateFuncCalculator دارد خلوت مي‌شود. قرار است هر كلاس يك كار را بيشتر انجام ندهد.
- برنامه از بسته بودن جهت توسعه هم خارج شده است (يكي ديگر از اصول طراحي شيءگرا). اگر تعاريف توابع محاسباتي را تماما در يك كلاس قرار دهيم صاحب اول و آخر آن كتابخانه خودمان خواهيم بود. اين كلاس بسته است جهت تغيير. اما با معرفي IAggregateFunc، من امروز 2 تابع را تعريف كرد‌ه‌ام، شما فردا توابع خاص خودتان را تعريف كنيد. باز هم برنامه كار خواهد كرد. نيازي نيست تا من هر روز يك نگارش جديد از كتابخانه را ارائه دهم كه در آن فقط يك تابع ديگر اضافه شده است.

اكنون يكي از چندين و چند روش بازنويسي كلاس AggregateFuncCalculator به صورت زير مي‌تواند باشد

public class AggregateFuncCalculator
{
        public decimal Calculate(IList<decimal> list, IAggregateFunc func)
        {
            return func.Calculate(list);
        }
}

بله! ديگر سوئيچي در كار نيست. اين كلاس تنها يك كار را انجام مي‌دهد. همچنين ديگر نيازي به تغيير هم ندارد (محاسبات از آن خارج شده) و باز است جهت توسعه (شما نگارش‌هاي دلخواه IAggregateFunc ديگر خود را توسعه داده و استفاده كنيد).

۱۳۹۰/۰۳/۰۴

آشنايي با Fluent interfaces


تعريف مقدماتي fluent interface در ويكي پديا به شرح زير است: (+)

In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a way of implementing an object oriented API in a way that aims to provide for more readable code.

به صورت خلاصه هدف آن فراهم آوردن روشي است كه بتوان متدها را زنجير وار فراخواني كرد و به اين ترتيب خوانايي كد نوشته شده را بالا برد. پياده سازي آن هم شامل دو نكته است:
الف) نوع متد تعريف شده بايد مساوي با نام كلاس جاري باشد.
ب) در اين حالت خروجي متد‌هاي ما كلمه كليدي this خواهند بود.

براي مثال:
using System;

namespace FluentInt
{
public class FluentApiTest
{
private int _val;

public FluentApiTest Number(int val)
{
_val = val;
return this;
}

public FluentApiTest Abs()
{
_val = Math.Abs(_val);
return this;
}

public bool IsEqualTo(int val)
{
return val == _val;
}
}
}
مثالي هم از استفاده‌ي آن به صورت زير مي‌تواند باشد:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
كه در آن توانستيم تمام متدها را زنجير وار و با خوانايي خوبي شبيه به نوشتن جملات انگليسي در كنار هم قرار دهيم.
خوب! اين مطلبي است كه همه جا پيدا مي‌كنيد و مطلب جديدي هم نيست. اما موردي را كه سخت مي‌شود يافت اين است كه طراحي كلاس فوق ايراد دارد. براي مثال شما مي‌توانيد تركيب‌هاي زير را هم تشكيل دهيد و كار مي‌كند؛ يا به عبارتي برنامه كامپايل مي‌شود و اين خوب نيست:
if(new FluentApiTest().Abs().Number(-10).IsEqualTo(10)) ...
if (new FluentApiTest().Abs().IsEqualTo(10)) ...
مي‌شود در كدهاي برنامه يك سري throw new exception را هم قرار داد كه ... هي! اول بايد اون رو فراخواني كني بعد اين رو!
ولي ... اين روش هم صحيح نيست. از ابتداي كار نبايد بتوان متد بي‌ربطي را در طول اين زنجيره مشاهده كرد. اگر قرار نيست استفاده گردد، نبايد هم در intellisense ظاهر شود و پس از آن هم نبايد قابل كامپايل باشد.

بنابراين صورت مساله به اين ترتيب اصلاح مي‌شود:
مي‌خواهيم پس از نوشتن FluentApiTest و قرار دادن يك نقطه، در intellisense فقط Number ظاهر شود و نه هيچ متد ديگري. پس از ذكر متد Number فقط متد Abs يا مواردي شبيه به آن مانند Sqrt ظاهر شوند. پس از انتخاب مثلا Abs آنگاه متد IsEqualTo توسط Intellisense قابل دسترسي باشد. در روش اول فوق، به صورت دوستانه همه چيز در دسترس است و هر تركيب قابل كامپايلي را مي‌شود با متدها ساخت كه اين مورد نظر ما نيست.
اينبار پياده سازي اوليه به شرح زير تغيير خواهد كرد:
using System;

namespace FluentInt
{
public class FluentApiTest
{
public MathMethods<FluentApiTest> Number(int val)
{
return new MathMethods<FluentApiTest>(this, val);
}
}

public class MathMethods<TParent>
{
private int _val;
private readonly TParent _parent;

public MathMethods(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public Restrictions<MathMethods<TParent>> Abs()
{
_val = Math.Abs(_val);
return new Restrictions<MathMethods<TParent>>(this, _val);
}
}

public class Restrictions<TParent>
{
private readonly int _val;
private readonly TParent _parent;

public Restrictions(TParent parent, int val)
{
_val = val;
_parent = parent;
}

public bool IsEqualTo(int val)
{
return _val == val;
}
}
}
در اينجا هم به همان كاربرد اوليه مي‌رسيم:
if (new FluentApiTest().Number(-10).Abs().IsEqualTo(10))
{
Console.WriteLine("Abs(-10)==10");
}
با اين تفاوت كه intellisense هربار فقط يك متد مرتبط در طول زنجيره را نمايش مي‌دهد و تمام متدها در همان ابتداي كار قابل انتخاب نيستند.
در پياده سازي كلاس MathMethods از Generics استفاده شده به اين جهت كه بتوان نوع متد Number را بر همين اساس تعيين كرد تا متدهاي كلاس MathMethods در Intellisense (يا به قولي در طول زنجيره مورد نظر) ظاهر شوند. كلاس Restrictions نيز به همين ترتيب معرفي شده است و از آن جهت تعريف نوع متد Abs استفاده كرديم. هر كلاس جديد در طول زنجيره، توسط سازنده خود به وهله‌اي از كلاس قبلي به همراه مقادير پاس شده دسترسي خواهد داشت. به اين ترتيب زنجيره‌اي را تشكيل داده‌ايم كه سازمان يافته است و نمي‌توان در آن متدي را بي‌جهت پيش يا پس از ديگري صدا زد و همچنين ديگر نيازي به بررسي نحوه‌ي فراخواني‌هاي يك مصرف كننده نيز نخواهد بود زيرا برنامه كامپايل نمي‌شود.

۱۳۸۹/۰۴/۱۴

MEF و الگوي Singleton


در مورد معرفي مقدماتي MEF مي‌توانيد به اين مطلب مراجعه كنيد و در مورد الگوي Singleton به اينجا.


كاربردهاي الگوي Singleton عموما به شرح زير هستند:
1) فراهم آوردن دسترسي ساده و عمومي به DAL (لايه دسترسي به داده‌ها)
2) دسترسي عمومي به امكانات ثبت وقايع سيستم در برنامه logging -
3) دسترسي عمومي به تنظيمات برنامه
و موارد مشابهي از اين دست به صورتيكه تنها يك روش دسترسي به اين اطلاعات وجود داشته باشد و تنها يك وهله از اين شيء در حافظه قرار گيرد.

با استفاده از امكانات MEF ديگر نيازي به نوشتن كدهاي ويژه توليد كلاس‌هاي Singleton نمي‌باشد زيرا اين چارچوب كاري دو نوع روش وهله سازي از اشياء (PartCreationPolicy) را پشتيباني مي‌كند: Shared و NonShared . حالت Shared دقيقا همان نام ديگر الگوي Singleton است. البته لازم به ذكر است كه حالت Shared ، حالت پيش فرض توليد وهله‌ها بوده و نيازي به ذكر صريح آن همانند ويژگي زير نيست:
[PartCreationPolicy(CreationPolicy.Shared)]

مثال:
فرض كنيد قرار است از كلاس زير تنها يك وهله بين صفحات يك برنامه‌ي Silverlight توزيع شود. با استفاده از ويژگي‌ Export به MEF اعلام كرده‌ايم كه قرار است سرويسي را ارائه دهيم :

using System;
using System.ComponentModel.Composition;

namespace SlMefTest
{
[Export]
public class WebServiceData
{
public int Result { set; get; }

public WebServiceData()
{
var rnd = new Random();
Result = rnd.Next();
}
}

}
اكنون براي اثبات اينكه تنها يك وهله از اين كلاس در اختيار صفحات مختلف قرار خواهد گرفت، يك User control جديد را به همراه يك دكمه كه مقدار Result را نمايش مي‌دهد به برنامه اضافه خواهيم كرد. دكمه‌ي ديگري را نيز به همين منظور به صفحه‌ي اصلي برنامه اضافه مي‌كنيم.
كدهاي صفحه اصلي برنامه (كه از يك دكمه و يك Stack panel جهت نمايش محتواي يوزر كنترل تشكيل شده) به شرح بعد هستند:
<UserControl x:Class="SlMefTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<Button Content="MainPageButton" Height="23"
HorizontalAlignment="Left"
Margin="10,10,0,0" Name="button1"
VerticalAlignment="Top" Width="98" Click="button1_Click" />
<StackPanel Name="panel1" Margin="5"/>
</StackPanel>
</UserControl>

using System.ComponentModel.Composition;
using System.Windows;

namespace SlMefTest
{
public partial class MainPage
{
[Import]
public WebServiceData Data { set; get; }

public MainPage()
{
InitializeComponent();
this.Loaded += mainPageLoaded;
}

void mainPageLoaded(object sender, RoutedEventArgs e)
{
CompositionInitializer.SatisfyImports(this);
panel1.Children.Add(new SilverlightControl1());
}

private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Data.Result.ToString());
}
}
}
با استفاده از ويژگي Import به MEF اعلام مي‌كنيم كه به اطلاعاتي از نوع شيء WebServiceData نياز داريم و توسط متد CompositionInitializer.SatisfyImports كار وهله سازي و پيوند زدن export و import هاي همانند صورت مي‌گيرد. سپس استفاده‌ي مستقيم از Data.Result مجاز بوده و مقدار آن null نخواهد بود.

كدهاي User control ساده اضافه شده به شرح زير هستند:

<UserControl x:Class="SlMefTest.SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White">
<Button Content="UserControlButton"
Height="23"
HorizontalAlignment="Left"
Margin="10,10,0,0"
Name="button1"
VerticalAlignment="Top"
Width="125"
Click="button1_Click" />
</Grid>
</UserControl>

using System.ComponentModel.Composition;
using System.Windows;

namespace SlMefTest
{
public partial class SilverlightControl1
{
[Import]
public WebServiceData Data { set; get; }

public SilverlightControl1()
{
InitializeComponent();
this.Loaded += silverlightControl1Loaded;
}

void silverlightControl1Loaded(object sender, RoutedEventArgs e)
{
CompositionInitializer.SatisfyImports(this);
}

private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Data.Result.ToString());
}
}
}
اكنون قبل از شروع برنامه يك break point را در سازنده‌ي كلاس WebServiceData قرار دهيد. سپس برنامه را آغاز نمائيد. تنها يكبار اين سازنده فراخواني خواهد شد (هر چند در دو كلاس كار Import اطلاعات WebServiceData صورت گرفته است). همچنين با كليك بر روي دو دكمه‌اي كه اكنون در صفحه‌ي اصلي برنامه ظاهر مي‌شوند، فقط يك عدد مشابه نمايش داده مي‌شود (با توجه به اينكه اطلاعات هر دكمه در يك وهله‌ي جداگانه قرار دارد؛ يكي متعلق است به صفحه‌ي اصلي و ديگري متعلق است به user control اضافه شده).

۱۳۸۹/۰۴/۱۱

انجام پي در پي اعمال Async به كمك Iterators - قسمت دوم


در قسمت قبل ايده‌ي اصلي و مفاهيم مرتبط با استفاده از Iterators مطرح شد. در اين قسمت به يك مثال عملي در اين مورد خواهيم پرداخت.

چندين كتابخانه و كلاس جهت مديريت Coroutines در دات نت تهيه شده كه ليست آن‌ها به شرح زير است:
1) Using C# 2.0 iterators to simplify writing asynchronous code
2) Wintellect's Jeffrey Richter's PowerThreading Library
3) Rob Eisenberg's Build your own MVVM Framework codes

و ...

مورد سوم كه توسط خالق اصلي كتابخانه‌ي Caliburn (يكي از فريم ورك‌هاي مشهور MVVM براي WPF و Silverlight) در كنفرانس MIX 2010 ارائه شد، اين روزها در وبلاگ‌هاي مرتبط بيشتر مورد توجه قرار گرفته و تقريبا به يك روش استاندارد تبديل شده است. اين روش از يك اينترفيس و يك كلاس به شرح زير تشكيل مي‌شود:

using System;

namespace SLAsyncTest.Helper
{
public interface IResult
{
void Execute();
event EventHandler Completed;
}
}

using System;
using System.Collections.Generic;

namespace SLAsyncTest.Helper
{
public class ResultEnumerator
{
private readonly IEnumerator<IResult> _enumerator;

public ResultEnumerator(IEnumerable<IResult> children)
{
_enumerator = children.GetEnumerator();
}

public void Enumerate()
{
childCompleted(null, EventArgs.Empty);
}

private void childCompleted(object sender, EventArgs args)
{
var previous = sender as IResult;

if (previous != null)
previous.Completed -= childCompleted;

if (!_enumerator.MoveNext())
return;

var next = _enumerator.Current;
next.Completed += childCompleted;
next.Execute();
}
}
}

توضيحات:
مطابق توضيحات قسمت قبل، براي مديريت اعمال همزمان به شكلي پي در پي، نياز است تا يك IEnumerable را به همراه yield return در پايان هر مرحله از كار ايجاد كنيم. در اينجا اين IEnumerable را از نوع اينترفيس IResult تعريف خواهيم كرد. متد Execute آن شامل كدهاي عمليات Async خواهند شد و پس از پايان كار رخداد Completed صدا زده مي‌شود. به اين صورت كلاس ResultEnumerator به سادگي مي‌تواند يكي پس از ديگري اعمال Async مورد نظر ما را به صورت متوالي فراخواني نمائيد. با هر بار فراخواني رخداد Completed، متد MoveNext صدا زده شده و يك مرحله به جلو خواهيم رفت.
براي مثال كدهاي ساده WCF Service زير را در نظر بگيريد.

using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Threading;

namespace SLAsyncTest.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{
[OperationContract]
public int GetNumber(int number)
{
Thread.Sleep(2000);//Simulating a log running operation
return number * 2;
}
}
}

قصد داريم در طي دو مرحله متوالي اين WCF Service را در يك برنامه‌ي Silverlight فراخواني كنيم. كدهاي قسمت فراخواني اين سرويس بر اساس پياده سازي اينترفيس IResult به صورت زير درخواهند آمد:

using System;
using SLAsyncTest.Helper;

namespace SLAsyncTest.Model
{
public class GetNumber : IResult
{
public int Result { set; get; }
public bool HasError { set; get; }

private int _num;
public GetNumber(int num)
{
_num = num;
}

#region IResult Members
public void Execute()
{
var srv = new TestServiceReference.TestServiceClient();
srv.GetNumberCompleted += (sender, e) =>
{
if (e.Error == null)
Result = e.Result;
else
HasError = true;

Completed(this, EventArgs.Empty); //run the next IResult
};
srv.GetNumberAsync(_num);
}

public event EventHandler Completed;
#endregion
}
}
در متد Execute كار فراخواني غيرهمزمان WCF Service به صورتي متداول انجام شده و در پايان متد Completed صدا زده مي‌شود. همانطور كه توضيح داده شد، اين فراخواني در كلاس ResultEnumerator ياد شده مورد استفاده قرار مي‌گيرد.
اكنون قسمت‌هاي اصلي كدهاي View Model برنامه به شكل زير خواهند بود:

private void doFetch(object obj)
{
new ResultEnumerator(executeAsyncOps()).Enumerate();
}

private IEnumerable<IResult> executeAsyncOps()
{
FinalResult = 0;
IsBusy = true; //Show BusyIndicator

//Sequential Async Operations
var asyncOp1 = new GetNumber(10);
yield return asyncOp1;

//using the result of the previous step
if(asyncOp1.HasError)
{
IsBusy = false; //Hide BusyIndicator
yield break;
}

var asyncOp2 = new GetNumber(asyncOp1.Result);
yield return asyncOp2;

FinalResult = asyncOp2.Result; //Bind it to the UI

IsBusy = false; //Hide BusyIndicator
}
در اينجا يك IEnumerable از نوع IResult تعريف شده است و در طي دو مرحله‌ي متوالي اما غيرهمزمان كار دريافت اطلاعات از WCF Service صورت مي‌گيرد. ابتدا عدد 10 به WCF Service ارسال مي‌شود و خروجي 20 خواهد بود. سپس اين عدد در مرحله‌ي بعد مجددا به WCF Service ارسال گرديده و حاصل نهايي كه عدد 40 مي‌باشد در اختيار سيستم Binding قرار خواهد گرفت.
اگر از اين روش استفاده نمي‌شد ممكن بود به اين جواب برسيم يا خير. ممكن بود مرحله‌ي دوم ابتدا شروع شود و سپس مرحله‌ي اول رخ دهد. اما با كمك Iterators و yield return به همراه كلاس ResultEnumerator موفق شديم تا عمليات دوم همزمان را در حالت تعليق قرار داده و پس از پايان اولين عمليات غير همزمان، مرحله‌ي بعدي فراخواني را بر اساس مقدار حاصل شده از WCF Service آغاز كنيم.
اين روش براي برنامه‌ نويس‌ها آشناتر است و همان سيستم فراخواني A->B->C را تداعي مي‌كند اما كليه اعمال غيرهمزمان هستند و ترد اصلي برنامه قفل نخواهد شد.

كدهاي كامل اين مثال را از اينجا مي‌توانيد دريافت كنيد.

۱۳۸۹/۰۴/۱۰

انجام پي در پي اعمال Async به كمك Iterators - قسمت اول


تقريبا تمام اعمال كار با شبكه در Silverlight از مدل asynchronous programming پيروي مي‌كنند؛ از فراخواني يك متد وب سرويس تا دريافت اطلاعات از وب و غيره. اگر در ساير فناوري‌هاي موجود در دات نت فريم ورك براي مثال جهت كار با يك وب سرويس هر دو متد همزمان و غيرهمزمان در اختيار برنامه نويس هستند اما اينجا خير. اينجا فقط روش‌هاي غيرهمزمان مرسوم هستند و بس. خيلي هم خوب. يك چارچوب كاري خوب بايد روش استفاده‌ي صحيح از كتابخانه‌هاي موجود را نيز ترويج كند و اين مورد حداقل در Silverlight اتفاق افتاده است.
براي مثال فراخواني‌هاي زير را در نظر بگيريد:
private int n1, n2;

private void FirstCall()
{
Service.GetRandomNumber(10, SecondCall);
}

private void SecondCall(int number)
{
n1 = number;
Service.GetRandomNumber(n1, ThirdCall);
}

private void ThirdCall(int number)
{
n2 = number;
// etc
}
عموما در اعمال Async پس از پايان عمليات در تردي ديگر، يك متد فراخواني مي‌گردد كه به آن callback delegate نيز گفته مي‌شود. براي مثال توسط اين سه متد قصد داريم اطلاعاتي را از يك وب سرويس دريافت و استفاده كنيم. ابتدا FirstCall فراخواني مي‌شود. پس از پايان كار آن به صورت خودكار متد SecondCall فراخواني شده و اين متد نيز يك عمليات Async ديگر را شروع كرده و الي آخر. در نهايت قصد داريم توسط مقادير بازگشت داده شده منطق خاصي را پياده سازي كنيم. همانطور كه مشاهده مي‌كنيد اين اعمال زيبا نيستند! چقدر خوب مي‌شد مانند دوران synchronous programming (!) فراخواني‌هاي اين متدها به صورت ذيل انجام مي‌شد:
private void FetchNumbers()
{
int n1 = Service.GetRandomNumber(10);
int n2 = Service.GetRandomNumber(n1);
}
در برنامه نويسي متداول هميشه عادت داريم كه اعمال به صورت A –> B –> C انجام شوند. اما در Async programming ممكن است ابتدا C انجام شود، سپس A و بعد B يا هر حالت ديگري صرفنظر از تقدم و تاخر آن‌ها در حين معرفي متدهاي مرتبط در يك قطعه كد. همچنين ميزان خوانايي اين نوع كدنويسي نيز مطلوب نيست. مانند مثال اول ذكر شده، يك عمليات به ظاهر ساده به چندين متد منقطع تقسيم شده است. البته به كمك lambda expressions مثال اول را به شكل زير نيز مي‌توان در طي يك متد ارائه داد اما اگر تعداد فراخواني‌ها بيشتر بود چطور؟ همچنين آيا استفاده از عدد n2 بلافاصله پس از عبارت ذكر شده مجاز است؟ آيا عمليات واقعا به پايان رسيده و مقدار مطلوب به آن انتساب داده شده است؟
private void FetchNumbers()
{
int n1, n2;

Service.GetRandomNumber(10, result =>
{
n1 = result;
Service.GetRandomNumber(n1, secondResult =>
{
n2 = secondResult;
});
});
}

به عبارتي مي‌خواهيم كل اعمال انجام شده در متد FetchNumbers هنوز Async باشند (ترد اصلي برنامه را قفل نكنند) اما پي در پي انجام شوند تا مديريت آن‌ها ساده‌تر شوند (هر لحظه دقيقا بدانيم كه كجا هستيم) و همچنين كدهاي توليدي نيز خواناتر باشند.
روش استانداري كه توسط الگوهاي برنامه نويسي براي حل اين مساله پيشنهاد مي‌شود، استفاده از الگوي coroutines است. توسط اين الگو مي‌توان چندين متد Async را در حالت معلق قرار داده و سپس در هر زماني كه نياز به آن‌ها بود عمليات آن‌ها را از سر گرفت.
دات نت فريم ورك حالت ويژه‌اي از coroutines را توسط Iterators پشتيباني مي‌كند (از C# 2.0 به بعد) كه در ابتدا نياز است از ديدگاه اين مساله مروري بر آن‌ها داشته باشيم. مثال بعد يك enumerator را به همراه yield return ارائه داده است:

using System;
using System.Collections.Generic;
using System.Threading;

namespace CoroutinesSample
{
class Program
{
static void printAll()
{
foreach (int x in integerList())
{
Console.WriteLine(x);
}
}

static IEnumerable<int> integerList()
{
yield return 1;
Thread.Sleep(1000);
yield return 2;
yield return 3;
}

static void Main()
{
printAll();
}
}
}

كامپايلر سي شارپ در عمل يك state machine را براي پياده سازي اين عمليات به صورت خودكار توليد خواهد كرد:

private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = 1;
this.<>1__state = 1;
return true;

case 1:
this.<>1__state = -1;
Thread.Sleep(0x3e8);
this.<>2__current = 2;
this.<>1__state = 2;
return true;

case 2:
this.<>1__state = -1;
this.<>2__current = 3;
this.<>1__state = 3;
return true;

case 3:
this.<>1__state = -1;
break;
}
return false;
}

در حين استفاده از يك IEnumerator ابتدا در وضعيت شيء Current آن قرار خواهيم داشت و تا زمانيكه متد MoveNext آن فراخواني نشود هيچ اتفاق ديگري رخ نخواهد داد. هر بار كه متد MoveNext اين enumerator فرخواني گردد (براي مثال توسط يك حلقه‌ي foreach) اجراي متد integerList ادامه خواهد يافت تا به yield return بعدي برسيم (ساير اعمال تعريف شده در حالت تعليق قرار دارند) و همينطور الي آخر.
از همين قابليت جهت مديريت اعمال Async پي در پي نيز مي‌توان استفاده كرد. State machine فوق تا پايان اولين عمليات تعريف شده صبر مي‌كند تا به yield return برسد. سپس با فراخواني متد MoveNext به عمليات بعدي رهنمون خواهيم شد. به اين صورت ديدگاهي پي در پي از يك سلسه عمليات غيرهمزمان حاصل مي‌گردد.

خوب ما الان نياز به يك كلاس داريم كه بتواند enumerator ايي از اين دست را به صورت خودكار مرحله به مرحله آن هم پس از پايان واقعي عمليات Async قبلي (يا مرحله‌ي قبلي)، اجرا كند. قبل از اختراع چرخ بايد متذكر شد كه ديگران اينكار را انجام داده‌اند و كتابخانه‌هاي رايگان و يا سورس بازي براي اين منظور موجود است.


ادامه دارد ...

۱۳۸۹/۰۲/۰۴

آشنايي با الگوي M-V-VM‌ - قسمت پنجم


در اين قسمت قصد داريم از امكانات جديد اعتبار سنجي تعريف شده در فضاي نام استاندارد System.ComponentModel.DataAnnotations استفاده نمائيم. از سيلورلايت سه به بعد امكان استفاده از اين فضاي نام به سادگي در برنامه‌هاي سيلورلايت ميسر است (همچنين در برنامه‌هاي ASP.Net MVC)؛ اما براي كار با آن در WPF نياز به تعدادي متد كمكي مي‌باشد...

فهرست مطالب:
فصل 5- تعيين اعتبار ورودي كاربر و الگوي MVVM
  • مقدمه
  • معرفي برنامه فصل
  • مدل برنامه‌ي فصل
  • ViewModel برنامه فصل
  • View برنامه فصل


دريافت قسمت پنجم
دريافت مثال قسمت پنجم


تعدادي از منابع و مآخذ مورد استفاده در اين سري:

1. Model-View-ViewModel (MVVM) Explained
2. Model View ViewModel
3. DataModel-View-ViewModel pattern
4. 5 Minute Overview of MVVM in Silverlight
5. A Field Guide to WPF Presentation Patterns
6. An attempt at simple MVVM with WPF
7. WPF: If Heineken did MVVM Frameworks Part 1 of n
8. Modal dialogs with MVVM and Silverlight 4
9. How do I do… With the Model-View-ViewModel pattern
10. Intro to WPF MVVM
11. Introduction to MVVM pattern in WPF
12. Learning WPF M-V-VM
13. Binding Combo Boxes in WPF with MVVM
14. Model-View-ViewModel Pattern
15. Unit Testable WCF Web Services in MVVM and Silverlight 4
16. MVVM Part 1: Overview
17. Which came first, the View or the Model?
18. Stackoverflow's questions tagged with MVVM
19. WPF: MVVM (Model View View-Model) Simplified
20. WPF and MVVM tutorial 01, Introduction
21. WPF patterns : MVC, MVP or MVVM or…?
22. Silverlight, MVVM and Validation Part III
23. DotNetKicks.com - Stories recently tagged with 'MVVM'
24. DotNetShoutout - Stories tagged with MVVM
25. MVVM Light Toolkit
26. MVVM screen casts
27. What’s new in MVVM Light V3
28. Using RelayCommands in Silverlight 3 and WPF
29. WPF Apps With The Model-View-ViewModel Design Pattern
30. WPF MVVM and Showing Dialogs


آشنايي با الگوي M-V-VM‌ - قسمت چهارم


در اين قسمت، MVVM Light Toolkit مورد بررسي قرار گرفته است (دريافت، نصب، به همراه ارائه 4 مثال جهت معرفي توانمند‌ي‌هاي آن)

فهرست مطالب:
فصل 4- آشنايي با MVVM Light Toolkit
  • ساير كتابخانه‌ها و Framework هاي موجود MVVM
  • نصب قالب‌هاي MVVM Light Toolkit مخصوص VS.Net 2008
  • نصب قالب‌هاي MVVM Light Toolkit مخصوص VS.Net 2010
  • نصب Code Snippets مجموعه MVVM Light Toolkit در VS.Net 2008/2010
  • نصب فايل‌هاي بايناري كتابخانه‌ي MVVM Light Toolkit
  • نصب قالب‌هاي MVVM Light Toolkit مخصوص Expression Blend
  • بررسي صحت نصب كتابخانه‌ي MVVM Light Toolkit
  • استفاده از Code Snippets نصب شده
  • مثال اول - بررسي RelayCommand
  • مثال دوم - بررسي Messenger
  • مثال سوم - بررسي Blendability
  • مثال چهارم - بررسي EventToCommand


دريافت قسمت چهارم
دريافت مثال‌هاي قسمت چهارم

۱۳۸۹/۰۲/۰۳

آشنايي با الگوي M-V-VM‌ - قسمت سوم


در اين قسمت، WPF MVVM Toolkit مايكروسافت به صورت كامل بررسي شده است (دريافت، نصب، ارائه يك مثال به همراه توضيحات و ايجاد آزمون‌هاي واحد).

فهرست مطالب:
فصل 3- آشنايي با WPF MVVM Toolkit
  • مقدمه
  • نصب WPF Model-View-ViewModel Toolkit
  • معرفي برنامه‌ي فصل
  • داده‌هاي برنامه
  • مدل برنامه
  • ViewModel برنامه
  • View برنامه
  • افزودن Command به برنامه
  • ايجاد آزمون‌هاي واحد

دريافت قسمت سوم
دريافت مثال قسمت سوم

۱۳۸۹/۰۲/۰۲

آشنايي با الگوي M-V-VM‌ - قسمت دوم


در اين قسمت، يك مثال ساده، بدون استفاده از فريم ورك‌هاي متداول M-V-VM بررسي شده است. در قسمت‌هاي بعدي با يك سري از فريم ورك‌هاي موجود آشنا خواهيم شد.

فهرست مطالب:
فصل 2- معرفي مثالي مقدماتي از پياده سازي الگوي M-V-VM در WPF
  • مقدمه
  • ساختار پوشه‌هاي يك برنامه‌ي MVVM
  • معرفي برنامه‌ي فصل
  • مدل برنامه
  • View برنامه
  • ViewModel برنامه

دريافت قسمت دوم
دريافت مثال قسمت دوم


۱۳۸۹/۰۲/۰۱

آشنايي با الگوي M-V-VM‌- قسمت اول


در مورد الگوي MVVM پيشتر دو مطلب در اين سايت منتشر شده‌اند : + و +
مشكل عمده‌اي هم كه در مورد اين الگو وجود دارد كمبود منابع آموزشي آن به زبان ساده است. هر چند اين الگو از طرف خود مايكروسافت ارائه شده اما همانند ASP.Net MVC به آن پر و بال ندادند و شاهد چند ده كتاب منتشر شده در مورد آن نيستيم.
به همين جهت خلاصه‌اي چند قسمتي را در اين مورد تهيه كرده‌ام كه در طي روزهاي آتي ارائه خواهند شد.

فهرست قسمت اول:
  • M-V-VM چيست؟
  • آشنايي با اجزاي مختلف الگوي M-V-VM
  • مزاياي استفاده از الگوي M-V-VM
  • اصول كاري و بايدها و نبايدهاي الگوي M-V-VM
  • بايدها و نبايدهاي يك View
  • بايدها و نبايدهاي ViewModel
  • بايدها و نبايدهاي Model
  • مروري بر معايب الگوي M-V-VM



۱۳۸۹/۰۱/۱۳

ويديوهاي آموزشي MVVM


يك سري ويديوي رايگان آموزشي MVVM از مايكروسافت و همچنين شركت Infragistics در دسترس هستند كه جهت سهولت، ليست آن‌ها را ادامه مي‌توانيد مشاهده نمائيد:


۱۳۸۸/۰۹/۲۷

تزريق وابستگي (dependency injection) به زبان ساده


اين مطلب در ادامه‌ي "آشنايي با الگوي IOC يا Inversion of Control (واگذاري مسئوليت)" مي‌باشد كه هر از چندگاهي يك قسمت جديد و يا كاملتر از آن ارائه خواهد شد.

==============
به صورت خلاصه ترزيق وابستگي و يا dependency injection ، الگويي است جهت تزريق وابستگي‌هاي خارجي يك كلاس به آن، بجاي استفاده مستقيم از آن‌ها در درون كلاس.
براي مثال شخصي را در نظر بگيريد كه قصد خريد دارد. اين شخص مي‌تواند به سادگي با كمك يك خودرو خود را به اولين محل خريد مورد نظر برساند. حال تصور كنيد كه 7 نفر عضو يك گروه، با هم قصد خريد دارند. خوشبختانه چون تمام خودروها يك اينترفيس مشخصي داشته و كار كردن با آن‌ها تقريبا شبيه به يكديگر است، حتي اگر از يك ون هم جهت رسيدن به مقصد استفاده شود، امكان استفاده و راندن آن همانند ساير خودروها مي‌باشد و اين دقيقا همان مطلبي است كه هدف غايي الگوي تزريق وابستگي‌ها است. بجاي اين‌كه هميشه محدود به يك خودرو براي استفاده باشيم، بنابر شرايط، خودروي متناسبي را نيز مي‌توان مورد استفاده قرار داد.
در دنياي نرم افزار، وابستگي كلاس Driver ، كلاس Car است. اگر موارد ذكر شده را بدون استفاده از تزريق وابستگي‌ها پياده سازي كنيم به كلاس‌هاي زير خواهيم رسيد:

//Person.cs
namespace DependencyInjectionForDummies
{
class Person
{
public string Name { get; set; }
}
}

//Car.cs
using System;
using System.Collections.Generic;

namespace DependencyInjectionForDummies
{
class Car
{
List<Person> _passengers = new List<Person>();

public void AddPassenger(Person p)
{
_passengers.Add(p);
Console.WriteLine("{0} added!", p.Name);
}

public void Drive()
{
foreach (var passenger in _passengers)
Console.WriteLine("Driving {0} ...!", passenger.Name);
}
}
}

//Driver.cs
using System.Collections.Generic;

namespace DependencyInjectionForDummies
{
class Driver
{
private Car _myCar = new Car();

public void DriveToMarket(IList<Person> passengers)
{
foreach (var passenger in passengers)
_myCar.AddPassenger(passenger);

_myCar.Drive();
}
}
}

//Program.cs
using System.Collections.Generic;
using System;

namespace DependencyInjectionForDummies
{
class Program
{
static void Main(string[] args)
{
new Driver().DriveToMarket(
new List<Person>
{
new Person{ Name="Ali" },
new Person{ Name="Vahid" }
});

Console.WriteLine("Press a key ...");
Console.ReadKey();
}
}
}

توضيحات:
كلاس شخص (Person) جهت تعريف مسافرين، اضافه شده؛ سپس كلاس خودرو (Car) كه اشخاص را مي‌توان به آن اضافه كرده و سپس به مقصد رساند، تعريف گرديده است. همچنين كلاس راننده (Driver) كه بر اساس ليست مسافرين، آن‌ها را به خودروي خاص ذكر شده هدايت كرده و سپس آن‌ها را با كمك كلاس خودرو به مقصد مي‌رساند؛ نيز تعريف شده است. در پايان هم يك كلاينت ساده جهت استفاده از اين كلاس‌ها ذكر شده است.
همانطور كه ملاحظه مي‌كنيد كلاس راننده به كلاس خودرو گره خورده است و اين راننده هميشه تنها از يك نوع خودروي مشخص مي‌تواند استفاده كند و اگر روزي قرار شد از يك ون كمك گرفته شود، اين كلاس بايد بازنويسي شود.

خوب! اكنون اگر اين كلاس‌ها را بر اساس الگوي تزريق وابستگي‌ها (روش تزريق در سازنده كه در قسمت قبل بحث شد) بازنويسي كنيم به كلاس‌هاي زير خواهيم رسيد:

//ICar.cs
using System;

namespace DependencyInjectionForDummies
{
interface ICar
{
void AddPassenger(Person p);
void Drive();
}
}

//Car.cs
using System;
using System.Collections.Generic;

namespace DependencyInjectionForDummies
{
class Car : ICar
{
//همانند قسمت قبل
}
}

//Van.cs
using System;
using System.Collections.Generic;

namespace DependencyInjectionForDummies
{
class Van : ICar
{
List<Person> _passengers = new List<Person>();

public void AddPassenger(Person p)
{
_passengers.Add(p);
Console.WriteLine("{0} added!", p.Name);
}

public void Drive()
{
foreach (var passenger in _passengers)
Console.WriteLine("Driving {0} ...!", passenger.Name);
}
}
}

//Driver.cs
using System.Collections.Generic;

namespace DependencyInjectionForDummies
{
class Driver
{
private ICar _myCar;

public Driver(ICar myCar)
{
_myCar = myCar;
}

public void DriveToMarket(IList<Person> passengers)
{
foreach (var passenger in passengers)
_myCar.AddPassenger(passenger);

_myCar.Drive();
}
}
}

//Program.cs
using System.Collections.Generic;
using System;

namespace DependencyInjectionForDummies
{
class Program
{
static void Main(string[] args)
{
Driver driver = new Driver(new Van());
driver.DriveToMarket(
new List<Person>
{
new Person{ Name="Ali" },
new Person{ Name="Vahid" }
});

Console.WriteLine("Press a key ...");
Console.ReadKey();
}
}
}

توضيحات:
در اينجا يك اينترفيس جديد به نام ICar اضافه شده است و بر اساس آن مي‌توان خودروهاي مختلفي را با نحوه‌ي بكارگيري يكسان اما با جزئيات پياده سازي متفاوت تعريف كرد. براي مثال در ادامه، يك كلاس ون با پياده سازي اين اينترفيس تشكيل شده است. سپس كلاس راننده‌ي ما بر اساس ترزيق اين اينترفيس در سازنده‌ي آن بازنويسي شده است. اكنون اين كلاس ديگر نمي‌داند كه دقيقا چه خودرويي را بايد مورد استفاده قرار دهد و از وابستگي مستقيم به نوعي خاص از آن‌ها رها شده است؛ اما مي‌داند كه تمام خودروها، اينترفيس مشخص و يكساني دارند. به تمام آن‌ها مي‌توان مسافراني را افزود و سپس به مقصد رساند. در پايان نيز يك راننده جديد بر اساس خودروي ون تعريف شده، سپس يك سري مسافر نيز تعريف گرديده و نهايتا متد DriveToMarket فراخواني شده است.
به اين صورت به يك سري كلاس اصطلاحا loosely coupled رسيده‌ايم. ديگر راننده‌ي ما وابسته‌ي به يك خودروي خاص نيست و هر زماني كه لازم بود مي‌توان خودروي مورد استفاده‌ي او را تغيير داد بدون اينكه كلاس راننده را بازنويسي كنيم.
يكي ديگر از مزاياي تزريق وابستگي‌ها ساده سازي unit testing كلاس‌هاي برنامه توسط mocking frameworks است. به اين صورت توسط اين نوع فريم‌ورك‌ها مي‌توان رفتار يك خودرو را تقليد كرد بجاي اينكه واقعا با تمام ريز جرئيات آن‌ها بخواهيم سروكار داشته باشيم (وابستگي‌ها را به صورت مستقل مي‌توان آزمايش كرد).

۱۳۸۸/۰۹/۲۵

آشنايي با الگوي IOC يا Inversion of Control (واگذاري مسئوليت)


كلاس Kid را با تعريف زير در نظر بگيريد. هدف از آن نگهداري اطلاعات فرزندان يك شخص خاص مي‌باشد:

namespace IOCBeginnerGuide
{
class Kid
{
private int _age;
private string _name;

public Kid(int age, string name)
{
_age = age;
_name = name;
}

public override string ToString()
{
return "KID's Age: " + _age + ", Kid's Name: " + _name;
}
}
}

اكنون كلاس والد را با توجه به اينكه در حين ايجاد اين شيء، فرزندان او نيز بايد ايجاد شوند؛ در نظر بگيريد:
using System;

namespace IOCBeginnerGuide
{
class Parent
{
private int _age;
private string _name;
private Kid _obj;

public Parent(int personAge, string personName, int kidsAge, string kidsName)
{
_obj = new Kid(kidsAge, kidsName);
_age = personAge;
_name = personName;
}

public override string ToString()
{
Console.WriteLine(_obj);
return "ParentAge: " + _age + ", ParentName: " + _name;
}
}
}

و نهايتا مثالي از استفاده از آن توسط يك كلاينت:

using System;

namespace IOCBeginnerGuide
{
class Program
{
static void Main(string[] args)
{
Parent p = new Parent(35, "Dev", 6, "Len");
Console.WriteLine(p);

Console.ReadKey();
Console.WriteLine("Press a key...");
}
}
}

كه خروجي برنامه در اين حالت مساوي سطرهاي زير مي‌باشد:

KID's Age: 6, Kid's Name: Len
ParentAge: 35, ParentName: Dev

مثال فوق نمونه‌اي از الگوي طراحي تركيب يا composition مي‌باشد كه به آن Object Dependency يا Object Coupling نيز گفته مي‌شود. در اين حالت ايجاد شيء والد وابسته است به ايجاد شيء فرزند.

مشكلات اين روش:
1- با توجه به وابستگي شديد والد به فرزند، اگر نمونه سازي از شيء فرزند در سازنده‌ي كلاس والد با موفقيت روبرو نشود، ايجاد نمونه‌ي والد با شكست مواجه خواهد شد.
2- با از بين رفتن شيء والد، فرزندان او نيز از بين خواهند رفت.
3- هر تغييري در كلاس فرزند، نياز به تغيير در كلاس والد نيز دارد (اصطلاحا به آن Dangling Reference هم گفته مي‌شود. اين كلاس آويزان آن كلاس است!).

چگونه اين مشكلات را برطرف كنيم؟
بهتر است كار وهله سازي از كلاس Kid به يك شيء، متد يا حتي فريم ورك ديگري واگذار شود. به اين واگذاري مسئوليت، delegation و يا inversion of control - IOC نيز گفته مي‌شود.

بنابراين IOC مي‌گويد كه:
1- كلاس اصلي (يا همان Parent) نبايد به صورت مستقيم وابسته به كلاس‌هاي ديگر باشد.
2- رابطه‌ي بين كلاس‌ها بايد بر مبناي تعريف كلاس‌هاي abstract باشد (و يا استفاده از interface ها).

تزريق وابستگي يا Dependency injection
براي پياده سازي IOC از روش تزريق وابستگي يا dependency injection استفاده مي‌شود كه مي‌تواند بر اساس constructor injection ، setter injection و يا interface-based injection باشد و به صورت خلاصه پياده سازي يك شيء را از مرحله‌ي ساخت وهله‌اي از آن مجزا و ايزوله مي‌سازد.

مزاياي تزريق وابستگي‌ها:
1- گره خوردگي اشياء را حذف مي‌كند.
2- اشياء و برنامه را انعطاف پذيرتر كرده و اعمال تغييرات به آن‌ها ساده‌تر مي‌شود.

روش‌هاي متفاوت تزريق وابستگي به شرح زير هستند:

تزريق سازنده يا constructor injection :
در اين روش ارجاعي از شيء مورد استفاده، توسط سازنده‌ي كلاس استفاده كننده از آن دريافت مي‌شود. براي نمونه در مثال فوق از آنجائيكه كلاس والد به كلاس فرزندان وابسته است، يك ارجاع از شيء Kid به سازنده‌ي كلاس Parent بايد ارسال شود.
اكنون بر اين اساس تعاريف، كلاس‌هاي ما به شكل زير تغيير خواهند كرد:

//IBuisnessLogic.cs
namespace IOCBeginnerGuide
{
public interface IBuisnessLogic
{
}
}

//Kid.cs
namespace IOCBeginnerGuide
{
class Kid : IBuisnessLogic
{
private int _age;
private string _name;

public Kid(int age, string name)
{
_age = age;
_name = name;
}

public override string ToString()
{
return "KID's Age: " + _age + ", Kid's Name: " + _name;
}
}
}

//Parent.cs
using System;

namespace IOCBeginnerGuide
{
class Parent
{
private int _age;
private string _name;
private IBuisnessLogic _refKids;

public Parent(int personAge, string personName, IBuisnessLogic obj)
{
_age = personAge;
_name = personName;
_refKids = obj;
}

public override string ToString()
{
Console.WriteLine(_refKids);
return "ParentAge: " + _age + ", ParentName: " + _name;
}
}
}

//CIOC.cs
using System;

namespace IOCBeginnerGuide
{
class CIOC
{
Parent _p;

public void FactoryMethod()
{
IBuisnessLogic objKid = new Kid(12, "Ren");
_p = new Parent(42, "David", objKid);
}

public override string ToString()
{
Console.WriteLine(_p);
return "Displaying using Constructor Injection";
}
}
}

//Program.cs
using System;

namespace IOCBeginnerGuide
{
class Program
{
static void Main(string[] args)
{
CIOC obj = new CIOC();
obj.FactoryMethod();
Console.WriteLine(obj);

Console.ReadKey();
Console.WriteLine("Press a key...");
}
}
}

توضيحات:
ابتدا اينترفيس IBuisnessLogic ايجاد خواهد شد. تنها متدهاي اين اينترفيس در اختيار كلاس Parent قرار خواهند گرفت.
از آنجائيكه كلاس Kid توسط كلاس Parent استفاده خواهد شد، نياز است تا اين كلاس نيز اينترفيس IBuisnessLogic را پياده سازي كند.
اكنون سازنده‌ي كلاس Parent بجاي ارجاع مستقيم به شيء Kid ، از طريق اينترفيس IBuisnessLogic با آن ارتباط برقرار خواهد كرد.
در كلاس CIOC كار پياده سازي واگذاري مسئوليت وهله سازي از اشياء مورد نظر صورت گرفته است. اين وهله سازي در متدي به نام Factory انجام خواهد شد.
و در نهايت كلاينت ما تنها با كلاس IOC سركار دارد.

معايب اين روش:
- در اين حالت كلاس business logic، نمي‌تواند داراي سازنده‌ي پيش فرض باشد.
- هنگاميكه وهله‌اي از كلاس ايجاد شد ديگر نمي‌توان وابستگي‌ها را تغيير داد (چون از سازنده‌ي كلاس جهت ارسال مقادير مورد نظر استفاده شده است).

تزريق تنظيم كننده يا Setter injection
اين روش از خاصيت‌ها جهت تزريق وابستگي‌ها بجاي تزريق آن‌ها به سازنده‌ي كلاس استفاده مي‌كند. در اين حالت كلاس Parent مي‌تواند داراي سازنده‌ي پيش فرض نيز باشد.

مزاياي اين روش:
- از روش تزريق سازنده بسيار انعطاف پذيرتر است.
- در اين حالت بدون ايجاد وهله‌اي مي‌توان وابستگي اشياء را تغيير داد (چون سر و كار آن با سازنده‌ي كلاس نيست).
- بدون نياز به تغييري در سازنده‌ي يك كلاس مي‌توان وابستگي اشياء را تغيير داد.
- تنظيم كننده‌ها داراي نامي با معناتر و با مفهوم‌تر از سازنده‌ي يك كلاس مي‌باشند.

نحوه‌ي پياده سازي آن:
در اينجا مراحل ساخت Interface و همچنين كلاس Kid با روش قبل تفاوتي ندارند. همچنين كلاينت نهايي استفاده كننده از IOC نيز مانند روش قبل است. تنها كلاس‌هاي IOC و Parent بايد اندكي تغيير كنند:

//Parent.cs
using System;

namespace IOCBeginnerGuide
{
class Parent
{
private int _age;
private string _name;

public Parent(int personAge, string personName)
{
_age = personAge;
_name = personName;
}

public IBuisnessLogic RefKID {set; get;}

public override string ToString()
{
Console.WriteLine(RefKID);
return "ParentAge: " + _age + ", ParentName: " + _name;
}
}
}

//CIOC.cs
using System;

namespace IOCBeginnerGuide
{
class CIOC
{
Parent _p;

public void FactoryMethod()
{
IBuisnessLogic objKid = new Kid(12, "Ren");
_p = new Parent(42, "David");
_p.RefKID = objKid;
}

public override string ToString()
{
Console.WriteLine(_p);
return "Displaying using Setter Injection";
}
}
}

همانطور كه ملاحظه مي‌كنيد در اين روش يك خاصيت جديد به نام RefKID به كلاس Parent اضافه شده است كه از هر لحاظ نسبت به روش تزريق سازنده با مفهوم‌تر و خود توضيح دهنده‌تر است. سپس كلاس IOC جهت استفاده از اين خاصيت اندكي تغيير كرده است.

ماخذ

۱۳۸۸/۰۹/۰۴

آشنايي با الگوي MVVM


حدود يك سال قبل الگوي MVVM زياد معروف نبود (Model-View-ViewModel pattern). اما در 6 ماه اخير، اين الگو به يك متدولوژي جدي توسعه برنامه‌هاي WPF و سيلورلايت تبديل شده. نمي‌شود به يك وبلاگ خوب WPF سر زد و خبري از اين روش نباشد. حتي فريم ورك‌هايي هم براي آن طراحي شده كه ليست آن‌ها را در اين مقاله مي‌توانيد مشاهده نمائيد.

مزاياي اين الگو چيست؟
  • جدا سازي Model و View
  • توليد كدهايي با قابليت تست بالا
  • فايل‌هاي code-behind ايي با حداقل كد
و ...

اگر علاقمند به آشنايي با اين الگوي طراحي باشيد ويديوي آموزشي زير در طي يك ساعت و نيم به توضيح اين مطلب پرداخته است.




ماخذ

۱۳۸۸/۰۶/۱۴

Dependency Injection


در ادامه مباحث بهتر كد بنويسيم و الگوهايي كه در اين رابطه معرفي شدند، اخيرا كتابي از انتشارات manning منتشر شده تحت عنوان Dependency Injection . هر چند به ظاهر اين كتاب براي جاوا كارها تهيه شده اما قسمت عمده‌اي از آن براي ساير زبان‌هاي برنامه نويسي ديگر نيز قابل استفاده است.




DESCRIPTION
In object-oriented programming, a central program normally controls other objects in a module, library, or framework. With dependency injection, this pattern is inverted—a reference to a service is placed directly into the object which eases testing and modularity. Spring or Google Guice use dependency injection so you can focus on your core application and let the framework handle infrastructural concerns.
Dependency Injection explores the DI idiom in fine detail, with numerous practical examples that show you the payoffs. You'll apply key techniques in Spring and Guice and learn important pitfalls, corner-cases, and design patterns. Readers need a working knowledge of Java but no prior experience with DI is assumed.

WHAT'S INSIDE:
◊ How to apply it (Understand it first!)
◊ Design patterns and nuances
◊ Spring, Google Guice, PicoContainer, and more
◊ How to integrate DI with Java frameworks


راستي، اين كتاب تر و تازه رو مي‌تونيد از همين كتاب فروشي‌هاي دور و اطراف نيز تهيه كنيد! در سايت booktraining دات ارگ در قسمت graphics-and-design به تاريخ 4 آگوست.



۱۳۸۸/۰۶/۰۶

در هم تنيدگي كدهاي خود را كمتر كنيد


مطلب "آشنايي با الگوي MVP" مقدمه‌ي كوتاهي بود بر يكي از روش‌هايي كه توسط آن مي‌توان گره خوردگي كدهاي خود را كمتر، نگهداري طولاني مدت و اعمال تغييرات بعدي به آن‌ها را ساده‌تر كرده و همچنين امكان استفاده مجدد از كدهاي موجود را فراهم آورد. در همين ارتباط ويديويي تحت عنوان Decoupling Your Code, By Example را مي‌توانيد از آدرس زير دريافت كنيد:



ماخذ



۱۳۸۸/۰۵/۲۸

آشنايي با الگوي MVP


پروژه‌هاي زيادي را مي‌توان يافت كه اگر سورس كدهاي آن‌ها را بررسي كنيم، يك اسپاگتي كد تمام عيار را در آن‌ها مي‌توان مشاهده نمود. منطق برنامه، قسمت دسترسي به داده‌ها، كار با رابط كاربر، غيره و غيره همگي درون كدهاي يك يا چند فرم خلاصه شده‌اند و آنچنان به هم گره خورده‌اند كه هر گونه تغيير يا اعمال درخواست‌هاي جديد كاربران، سبب از كار افتادن قسمت ديگري از برنامه مي‌شود.
همچنين از كدهاي حاصل در يك پروژه، در پروژه‌‌هاي ديگر نيز نمي‌توان استفاده كرد (به دليل همين در هم تنيده بودن قسمت‌هاي مختلف). حداقل نتيجه يك پروژه براي برنامه نويس، بايد يك يا چند كلاس باشد كه بتوان از آن به عنوان ابزار تسريع انجام پروژه‌هاي ديگر استفاده كرد. اما در يك اسپاگتي كد، بايد مدتي طولاني را صرف كرد تا بتوان يك متد را از لابلاي قسمت‌هاي مرتبط و گره خورده با رابط كاربر استخراج و در پروژه‌اي ديگر استفاده نمود. براي نمونه آيا مي‌توان اين كدها را از يك برنامه ويندوزي استخراج كرد و آن‌ها را در يك برنامه تحت وب استفاده نمود؟

يكي از الگوهايي كه شيوه‌ي صحيح اين جدا سازي را ترويج مي‌كند، الگوي MVP يا Model-View-Presenter مي‌باشد. خلاصه‌ي اين الگو به صورت زير است:


Model :
من مي‌دانم كه چگونه اشياء برنامه را جهت حصول منطقي خاص، پردازش كنم.
من نمي‌دانم كه چگونه بايد اطلاعاتي را به شكلي بصري به كاربر ارائه داد يا چگونه بايد به رخ‌دادها يا اعمال صادر شده از طرف كاربر پاسخ داد.

View :
من مي‌دانم كه چگونه بايد اطلاعاتي را به كاربر به شكلي بصري ارائه داد.
من مي‌دانم كه چگونه بايد اعمالي مانند data binding و امثال آن را انجام داد.
من نمي‌دانم كه چگونه بايد منطق پردازشي موارد ذكر شده را فراهم آورم.

Presenter :
من مي‌دانم كه چگونه بايد درخواست‌هاي رسيده كاربر به View را دريافت كرده و آن‌ها را به Model‌ انتقال دهم.
من مي‌دانم كه چگونه بايد اطلاعات را به Model ارسال كرده و سپس نتيجه‌ي پردازش آن‌ها را جهت نمايش در اختيار View قرار دهم.
من نمي‌دانم كه چگونه بايد اطلاعاتي را ترسيم كرد (مشكل View است نه من) و نمي‌دانم كه چگونه بايد پردازشي را بر روي اطلاعات انجام دهم. (مشكل Model است و اصلا ربطي به اينجانب ندارد!)


يك مثال ساده از پياده سازي اين روش
برنامه‌اي وبي را بنويسيد كه پس از دريافت شعاع يك دايره از كاربر، مساحت ‌آن‌را محاسبه كرده و نمايش دهد.
يك تكست باكس در صفحه قرار خواهيم داد (txtRadius) و يك دكمه جهت دريافت درخواست كاربر براي نمايش نتيجه حاصل در يك برچسب به نام lblResult

الف) پياده سازي به روش متداول (اسپاگتي كد)

protected void btnGetData_Click(object sender, EventArgs e)
{
lblResult.Text = (Math.PI * double.Parse(txtRadius.Text) * double.Parse(txtRadius.Text)).ToString();
}
بله! كار مي‌كنه!
اما اين مشكلات را هم دارد:
- منطق برنامه (روش محاسبه مساحت دايره) با رابط كاربر گره خورده.
- كدهاي برنامه در پروژه‌ي ديگري قابل استفاده نيست. (شما متد يا كلاسي را اين‌جا با قابليت استفاده مجدد مي‌توانيد پيدا مي‌كنيد؟ آيا يكي از اهداف برنامه نويسي شيءگرا توليد كدهايي با قابليت استفاده مجدد نبود؟)
- چگونه بايد براي آن آزمون واحد نوشت؟

ب) بهبود كد و جدا سازي لايه‌ها از يكديگر

در روش MVP متداول است كه به ازاي هر يك از اجزاء ابتدا يك interface نوشته شود و سپس اين اينترفيس‌ها پياده سازي گردد.

پياده سازي منطق برنامه:

1- ايجاد Model :
يك فايل جديد را به نام CModel.cs به پروژه اضافه كرده و كد زير را به آن خواهيم افزود:

using System;

namespace MVPTest
{
public interface ICircleModel
{
double GetArea(double radius);
}

public class CModel : ICircleModel
{
public double GetArea(double radius)
{
return Math.PI * radius * radius;
}
}
}
همانطور كه ملاحظه مي‌كنيد اكنون منطق برنامه از موارد زير اطلاعي ندارد:
- خبري از textbox و برچسب و غيره نيست. اصلا نمي‌داند كه رابط كاربري وجود دارد يا نه.
- خبري از رخ‌دادهاي برنامه و پاسخ دادن به آن‌ها نيست.
- از اين كد مي‌توان مستقيما و بدون هيچ تغييري در برنامه‌هاي ديگر هم استفاده كرد.
- اگر باگي در اين قسمت وجود دارد، تنها اين كلاس است كه بايد تغيير كند و بلافاصله كل برنامه از اين بهبود حاصل شده مي‌تواند بدون هيچگونه تغييري و يا به هم ريختگي استفاده كند.
- نوشتن آزمون واحد براي اين كلاس كه هيچگونه وابستگي به UI ندارد ساده است.


2- ايجاد View :
فايل ديگري را به نام CView.cs را به همراه اينترفيس زير به پروژه اضافه مي‌كنيم:

namespace MVPTest
{
public interface IView
{
string RadiusText { get; set; }
string ResultText { get; set; }
}
}

كار View دريافت ابتدايي مقادير از كاربر توسط RadiusText و نمايش نهايي نتيجه توسط ResultText است البته با يك اما.
View نمي‌داند كه چگونه بايد اين پردازش صورت گيرد. حتي نمي‌داند كه چگونه بايد اين مقادير را به Model جهت پردازش برساند يا چگونه آن‌ها را دريافت كند (به همين جهت از اينترفيس براي تعريف آن استفاده شده).

3- ايجاد Presenter :
در ادامه فايل جديدي را به نام CPresenter.cs‌ با محتويات زير به پروژه خواهيم افزود:

namespace MVPTest
{
public class CPresenter
{
IView _view;

public CPresenter(IView view)
{
_view = view;
}

public void CalculateCircleArea()
{
CModel model = new CModel();
_view.ResultText = model.GetArea(double.Parse(_view.RadiusText)).ToString();
}
}
}

كار اين كلاس برقراري ارتباط با Model است.
مي‌داند كه چگونه اطلاعات را به Model ارسال كند (از طريق _view.RadiusText) و مي‌داند كه چگونه نتيجه‌ي پردازش را در اختيار View قرار دهد. (با انتساب آن به _view.ResultText)
نمي‌داند كه چگونه بايد اين پردازش صورت گيرد (كار مدل است نه او). نمي‌داند كه نتيجه‌ي نهايي را چگونه نمايش دهد (كار View است نه او).
روش معرفي View به اين كلاس به constructor dependency injection معروف است.

اكنون كد وب فرم ما كه در قسمت (الف) معرفي شده به صورت زير تغيير مي‌كند:

using System;

namespace MVPTest
{
public partial class _Default : System.Web.UI.Page, IView
{
protected void Page_Load(object sender, EventArgs e)
{
}

public string RadiusText
{
get { return txtRadius.Text; }
set { txtRadius.Text = value; }
}
public string ResultText
{
get { return lblResult.Text; }
set { lblResult.Text = value; }
}

protected void btnGetData_Click(object sender, EventArgs e)
{
CPresenter presenter = new CPresenter(this);
presenter.CalculateCircleArea();
}
}
}

در اين‌جا يك وهله از Presenter براي برقراري ارتباط با Model ايجاد مي‌شود. همچنين كلاس وب فرم ما اينترفيس View را نيز پياده سازي خواهد كرد.