۱۳۸۹/۰۴/۱۴

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 اضافه شده).