آیا یک توسعه دهندهی وب میتواند یک طراح وب هم باشد؟
Blazer یک فریمورک جدید تحت وب هست که این امکان را به برنامه نویسان دات نت میدهد تا از طریق Open Web Standards بتوانند کدهای خود را در مرورگر اجرا و تجربه جدیدی از ساخت برنامههای تک صفحهای را داشته باشند. در این نوشتار قصد داریم ساختار و نحوه کارکرد این فناوری را بررسی نماییم. قبل از هر چیزی به دوران قبل از ایجاد Web Assembly برمیگردیم :
همانطور که در شکل زیر میبینید، زمانی تنها جاوااسکریپت فرمانروای یک مرورگر محسوب میشد. در این حالت کدهای جاوااسکریپت به هر شکلی که نوشته شده باشند در اختیار parser قرار میگیرند و یک درخت از کدهای نوشته شده ایجاد شده و از طریق یک کامپایلر، کدها به سطح پایینتری مشابه بایت کدها تبدیل میگردند و سپس از طریق یک مفسر دسترسی به بخشهای مختلف api یک مرورگر در اختیار این کدها قرار میگیرند تا کار مورد نظر انجام شود.
در تصویر بعدی Web Assembly به بخش مفسر تزریق میشود و از طریق آن زبانهای مختلف باید بر اساس Web Standard، به کدهای سطحهای پایینتری کامپایل شوند. در اینجا این نکته مدنظر باشد که کدهایی که به سطح پایینتری کامپایل میشوند، تنها در داخل مرورگر شناخته شده میباشند و در خارج از دنیای وب قابل استفاده نیستند و نمیتوانند در سطح سیستم عامل قابل اجرا باشند. به همین جهت به شکل یک sandbox مورد استفاده قرار میگیرند و از این لحاظ، مشکلات امنیتی را در خارج از مرورگر ایجاد نمیکنند.
در شکل سوم Blazor که ترکیبی از
نام Browser + Razor
میباشد اضافه میشود. Blazor در
اینجا وظیفه دارد محتوای فایل دریافتی را که شامل کدهای HTML و CSS و
جاوااسکریپت است، به کدهای قابل فهمی برای مرورگر تبدیل کند. سپس mono وارد کار میشود. همانطور که میدانید mono جهت پشتیبانی از اجرای چندسکویی پروژههای دات نت اضافه شده که در اینجا هم همان وظیفه را منتها برای مرورگرهای مختلف، دارد. بدین جهت مونوی کامپایل شده بر روی Web Assembly قرار میگیرد تا کدهای دریافتی را تفسیر نماید. Blazor در اینجا dllهای لازم را در mono بارگذاری میکند و سپس mono کدها را برای Web Assembly تفسیر میکند.
کلاس 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 جهت استفاده از این خاصیت اندکی تغییر کرده است.
ماخذ
حذف یک ردیف از اطلاعات به همراه پویانمایی محو شدن اطلاعات آن توسط jQuery در ASP.NET MVC
من این خط را حذف نکرده بودم
contentType: "application/json; charset=utf-8",
باگ IE 10 برای کار با jQuery Ajax
خلاصه اشتراکهای روز سه شنبه 17 آبان 1390
"
بسیار جالب بود. (اگر خاطر داشته باشید این لینک را 7 8 ماه پیش در نظرات وبلاگ آقای محبی معرفی کرده بودید !)
--
و طبق کامنتهای فوق، داخل Contractor رو هم اینطور تعریف میکنم:
_stringLocalizer = stringLocalizerFactory.Create( baseName: "Controllers.LoginController", location: "Zagros.Resources"); _htmlLocalizer = htmlLocalizerFactory.Create( baseName: "Controllers.LoginController", location: "Zagros.Resources");
ولی خب مقدار مورد نظر داخل فایل منبع برگردانده نمیشود؟
«به چه صورت جایگزین کنم»
مثل مثال بالا که از فایل کانفیگ گذاشته شد، قسمت provider connection string رو پیدا کن و جایگزینش کن با رشته اتصالی که به شما دادند.
«ازین روشی که گفتین اگه بخوام استفاده کنم»
این روش کاری به سناریوی شما نداره. برای جایی هست که برنامه قراره مثلا برای هر سال به یک دیتابیس مجزا وصل بشه. اول کار، کاربر باید انتخاب کنه که مثلا به دیتابیس سال 89 وصل بشه یا دیتابیس سال 92.