در طي چند مقاله قصد بررسي نحوهي توليد برنامههاي توسعه پذير (extensible) را با استفاده از plug-ins و يا add-ins داريم.
افزونهها عموما در سه گروه قرار ميگيرند:
الف) افزونه، سرويسي را به هاست ارائه ميدهد. براي مثال يك ميل سرور نياز به افزونههايي براي ويروس يابي يا فيلتر كردن هرزنامهها دارد؛ يا يك برنامه پردازش متني نياز به افزونهاي جهت بررسي غلطهاي املايي ميتواند داشته باشد و يا يك مرورگر وب ميتواند با كمك افزونهها قابليتهاي پيش فرض خود را به شدت توسعه و افزايش دهد (نمونهي بارز آن فايركس است كه عمدهترين دليل اقبال عمومي به آن سهولت توسعه پذيري آن ميباشد).
ب) در گروه دوم، هاست، رفتار مشخصي را ارائه داده و سپس افزونه بر اساس آن، نحوهي عملكرد هاست را مشخص ميكند. در اين حالت هاست است كه سرويسي را به افزونه ارائه ميدهد. نمونهي بازر آن افزونههاي آفيس هستند كه امكان اتوماسيون فرآيندهاي مختلف آنرا ميسر ميسازند. به اين صورت امكان توسعهي يك برنامه به شكلي كه در طراحي اوليه آن اصلا انتظار آن نميرفته وجود خواهد داشت. همچنين در اينجا نيازي به داشتن سورس كد برنامهي اصلي نيز نميباشد.
ج) گروه سوم افزونهها تنها از هاست جهت نمايش خود استفاده كرده و عملا استفادهي خاصي از هاست ندارد. براي مثال نوار ابزاري كه خود را به windows explorer متصل ميكند و تنها از آن جهت نمايش خود بهره ميجويد.
در حال حاضر حداقل دو فريم ورك عمده جهت انجام اينكار و توليد افزونهها براي دات نت فريم ورك مهيا است:
الف) managed addin framework يا MAF
ب) managed extensibility framework يا MEF
فضاي نام جديدي به دات نت فريم ورك سه و نيم به نام System.AddIn اضافه شده است كه به آن Managed AddIn Framework يا MAF نيز اطلاق ميشود. از اين فريم ورك در VSTO (توليد افزونه براي مجموعهي آفيس) توسط خود مايكروسافت استفاده شده است.
فريم ورك توسعهي افزونههاي مديريت شده در دات نت فريم ورك سه و نيم، مزاياي زير را در اختيار ما خواهد گذاشت:
- امكانات load و unload افزونههاي توليد شده
- امكان تغيير افزونهها در زمان اجراي برنامه اصلي بدون نياز به بستن آن
- ارائهي محيطي ايزوله با ترسيم مرزي بين افزونه و برنامه اصلي
- مديريت طول عمر افزونه
- مديريت سازگاري با نگارشهاي قبلي و يا بعدي يك افزونه
- امكانات به اشتراك گذاري افزونهها با برنامههاي ديگر
- تنظيمات امنيتي و مشخص سازي سطح دسترسي افزونهها
و ...
يك راه حل مبتني بر MAF ميتواند شامل 7 پروژه باشد (كه به روابط تعريف شده در آن pipeline هم گفته ميشود):
Host : همان برنامهي اصلي است كه توسط يك سري افزونه، توسعه يافته است.
Host View : بيانگر انتظارات هاست از افزونهها است. به عبارت ديگر افزونهها بايد موارد ليست شده در اين پروژه را پياده سازي كنند.
Host Side Adapter : پل ارتباطي Host View و پروژهي Contract است.
Contract: اينترفيسي است كه كار برقراري ارتباط بين Host و افزونهها را برعهده دارد.
Add-In Side Adapter : پل ارتباطي بين Add-In View و Contract است.
Add-In View : حاوي متدها و اشيايي است كه جهت برقراري ارتباط با هاست از آنها استفاده ميشود.
Add-In : اسمبلي است كه توسط هاست جهت توسعهي قابليتهاي خود بارگذاري ميشود (به آن Add-On ، Extension ، Plug-In و Snap-In هم گفته ميشود).
هدف از اين جدا سازيها ارائهي راه حل loosely-coupledايي است كه امكان ايزوله سازي، اعمال شرايط امنيتي ويژه و همچنين كنترل نگارشهاي مختلف را تسهيل ميبخشد و اين امر با استفاده از interface هاي معرفي شده ميسر گرديده است. اين pipeline از قسمتهاي ذيل تشكيل ميشود:
قرار داد يا Contract
براي توليد يك افزونه نياز است تا بين هاست و افزونه قراردادي بسته شود. با توجه به استفاده از MAF ، روش تعريف اين قرار داد براي مثال در يك افزونهي مترجم به صورت زير بايد باشد:
[AddInContract]
public interface ITranslator : IContract
{
string Translate(string input);
}
استفاده از ويژگي AddInContract و پياده سازي اينترفيس IContract جزو مراحل كاري استفاده از MAF است. MAF هنگام توليد پوياي pipeline ذكر شده به دنبال ويژگي AddInContract ميگردد. اين موارد در فضاي نام System.AddIn.Pipeline تعريف شدهاند.
ديدگاهها يا Views
ديدگاهها كدهايي هستند كه كار تعامل مستقيم بين افزونه و هاست را بر عهده دارند. هاست يا افزونه هر كدام ميتوانند ديدگاه خود را نسبت به قرار داد بسته شده داشته باشند. اين موارد نيز همانند قرار داد در اسمبليهاي مجزايي نگهداري ميشوند.
ديدگاه هاست نسبت به قرار داد:
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
[AddInBase]
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
هر دو كلاس فوق بر اساس قرار موجود بنا ميشوند اما وابسته به آن نيستند. به همين جهت به صورت كلاسهايي abstract تعريف شدهاند. در سمت افزونه، كلاس تعريف شده ديدگاه آن با كلاس ديدگاه سمت هاست تقريبا يكسان ميباشد؛ اما با ويژگي AddInBase تعريف شده در فضاي نام System.AddIn.Pipeline مزين گرديده است.
وفق دهندهها يا Adapters
آخرين قسمت pipeline ، وفق دهندهها هستند كه كار آنها اتصال قرار داد به ديدگاهها است و توسط آن مديريت طول عمر افزونه و همچنين تبديل اطلاعات بين قسمتهاي مختلف انجام ميشود. شايد در نگاه اول وجود آنها زائد به نظر برسد اما اين جدا سازي كدها سبب توليد افزونههايي خواهد شد كه به نگارش هاست و برنامه اصلي وابسته نبوده و بر عكس (version tolerance). به دو كلاس زير دقت نمائيد:
كلاس زير با ويژگي [HostAdapter] تعريف شده در فضاي نام System.AddIn.Pipeline، مزين شده است و كار آن اتصال HostView به Contract ميباشد. براي اين منظور TranslatorHostView ايي را كه پيشتر معرفي كرديم بايد پياده سازي نمايد. علاوه بر اين با ايجاد وهلهاي از كلاس ContractHandle ، كار مديريت طول عمر افزونه را نيز ميتوان انجام داد.
[HostAdapter]
public class TranslatorHostViewToContract : TranslatorHostView
{
ITranslator _contract;
ContractHandle _lifetime;
public TranslatorHostViewToContract(ITranslator contract)
{
_contract = contract;
_lifetime = new ContractHandle(contract);
}
public override string Translate (string inp)
{
return _contract.Translate(inp);
}
}
[AddInAdapter]
public class TranslatorAddInViewToContract : ContractBase, ITranslator
{
TranslatorAddInView _view;
public TranslatorAddInViewToContract(TranslatorView view)
{
_view = view;
}
public string Translate(string inp)
{
return _view.Translate(inp);
}
}
قسمت عمدهاي از اين كدها تكراري است. جهت سهولت توليد اين كلاسها و پروژههاي مرتبط، تيم مربوطه برنامهاي را به نام pipeline builder ارائه داده است كه از آدرس زير قابل دريافت است:
اين برنامه با دريافت اسمبلي مربوط بهcontract ، كار ساخت خودكار كلاسهاي adapters و views را انجام خواهد داد.
ايجاد افزونه
پس از ساخت قسمتهاي مختلف pipeline ، اكنون ميتوان افزونه را ايجاد نمود. هر افزونه بايد add-in view را پياده سازي كرده و با ويژگي AddIn مزين شود. براي مثال:
[AddIn("GoogleTranslator", Description="Universal translator",
Version="1.0.0.0", Publisher="YourName")]
public class GoogleAddIn : TranslatorAddInView
{
public string Translate(string input)
{
...
}
}
ادامه دارد ....