UI
در نهایت نوبت به طراحی و کدنویسی UIمیرسد تا بتوانیم محصولات را به کاربر نمایش دهیم. اما قبل از شروع باید موضوعی را یادآوری کنم. اگر به یاد داشته باشید، در کلاس ProductServiceموجود در لایهی Domain، از طریق یکی از روشهای الگوی Dependency Injectionبه نام Constructor Injection، فیلدی از نوع IProductRepositoryرا مقداردهی نمودیم. حال زمانی که بخواهیم نمونه ای را از ProductServiceایجاد نماییم، باید به عنوان پارامتر ورودی سازنده، شی ایجاد شده از جنس کلاس ProductRepositoryموجود در لایه Repositoryرا به آن ارسال نماییم. اما از آنجایی که میخواهیم تفکیک پذیری لایهها از بین نرود و UIبسته به نیاز خود، نمونه مورد نیاز را ایجاد نموده و به این کلاس ارسال کند، از ابزارهایی برای این منظور استفاده میکنیم. یکی از این ابزارها StructureMapمیباشد که یک Inversion of Control Containerیا به اختصار IoC Containerنامیده میشود. با Inversion of Controlدر مباحث بعدی بیشتر آشنا خواهید شد. StructureMapابزاری است که در زمان اجرا، پارامترهای ورودی سازندهی کلاسهایی را که از الگوی Dependency Injectionاستفاده نموده اند و قطعا پارامتر ورودی آنها از جنس یک Interfaceمیباشد را، با ایجاد شی مناسب مقداردهی مینماید.
به منظور استفاده از StructureMapدر Visual Studio 2012باید بر روی پروژه UIخود کلیک راست نموده و گزینهی Manage NuGet Packagesرا انتخاب نمایید. در پنجره ظاهر شده و از سمت چپ گزینهی Onlineو سپس در کادر جستجوی سمت راست و بالای پنجره واژهی structuremapرا جستجو کنید. توجه داشته باشید که باید به اینترنت متصل باشید تا بتوانید Packageمورد نظر را نصب نمایید. پس از پایان عمل جستجو، در کادر میانی structuremapظاهر میشود که میتوانید با انتخاب آن و فشردن کلید Installآن را بر روی پروژه نصب نمایید.
جهت آشنایی بیشتر با NuGetو نصب آن در سایر نسخههای Visual Studioمیتوانید به لینکهای زیر رجوع کنید:
کلاسی با نام BootStrapperرا با کد زیر به پروژه UIخود اضافه کنید:
using StructureMap; using StructureMap.Configuration.DSL; using SoCPatterns.Layered.Repository; using SoCPatterns.Layered.Model; namespace SoCPatterns.Layered.WebUI { public class BootStrapper { public static void ConfigureStructureMap() { ObjectFactory.Initialize(x => x.AddRegistry<ProductRegistry>()); } } public class ProductRegistry : Registry { public ProductRegistry() { For<IProductRepository>().Use<ProductRepository>(); } } }
ممکن است یک WinUIایجاد کرده باشید که در این صورت به جای فضای نام SoCPatterns.Layered.WebUIاز SoCPatterns.Layered.WinUIاستفاده نمایید.
هدف کلاس BootStrapperاین است که تمامی وابستگیها را توسط StructureMapدر سیستم Registerنماید. زمانی که کدهای کلاینت میخواهند به یک کلاس از طریق StructureMapدسترسی داشته باشند، Structuremapوابستگیهای آن کلاس را تشخیص داده و بصورت خودکار پیاده سازی واقعی (Concrete Implementation)آن کلاس را، براساس همان چیزی که ما برایش تعیین کردیم، به کلاس تزریق مینماید. متد ConfigureStructureMapباید در همان لحظه ای که Applicationآغاز به کار میکند فراخوانی و اجرا شود. با توجه به نوع UIخود یکی از روالهای زیر را انجام دهید:
در WebUI:
فایل Global.asaxرا به پروژه اضافه کنید و کد آن را بصورت زیر تغییر دهید:
namespace SoCPatterns.Layered.WebUI { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { BootStrapper.ConfigureStructureMap(); } } }
در WinUI:
در فایل Program.csکد زیر را اضافه کنید:
namespace SoCPatterns.Layered.WinUI { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false);BootStrapper.ConfigureStructureMap(); Application.Run(new Form1()); } } }
سپس برای طراحی رابط کاربری، با توجه به نوع UIخود یکی از روالهای زیر را انجام دهید:
در WebUI:
صفحه Default.aspxرا باز نموده و کد زیر را به آن اضافه کنید:
<asp:DropDownList AutoPostBack="true" ID="ddlCustomerType" runat="server"><asp:ListItem Value="0">Standard</asp:ListItem><asp:ListItem Value="1">Trade</asp:ListItem></asp:DropDownList><asp:Label ID="lblErrorMessage" runat="server" ></asp:Label><asp:Repeater ID="rptProducts" runat="server" ><HeaderTemplate><table><tr><td>Name</td><td>RRP</td><td>Selling Price</td><td>Discount</td><td>Savings</td></tr><tr><td colspan="5"><hr /></td></tr></HeaderTemplate><ItemTemplate><tr><td><%# Eval("Name") %></td><td><%# Eval("RRP")%></td><td><%# Eval("SellingPrice") %></td><td><%# Eval("Discount") %></td><td><%# Eval("Savings") %></td></tr></ItemTemplate><FooterTemplate></table></FooterTemplate></asp:Repeater>
در WinUI:
فایل Form1.Designer.csرا باز نموده و کد آن را بصورت زیر تغییر دهید:
#region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.cmbCustomerType = new System.Windows.Forms.ComboBox(); this.dgvProducts = new System.Windows.Forms.DataGridView(); this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colRrp = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colSellingPrice = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colDiscount = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.colSavings = new System.Windows.Forms.DataGridViewTextBoxColumn(); ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).BeginInit(); this.SuspendLayout(); // // cmbCustomerType // this.cmbCustomerType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbCustomerType.FormattingEnabled = true; this.cmbCustomerType.Items.AddRange(new object[] { "Standard", "Trade"}); this.cmbCustomerType.Location = new System.Drawing.Point(12, 90); this.cmbCustomerType.Name = "cmbCustomerType"; this.cmbCustomerType.Size = new System.Drawing.Size(121, 21); this.cmbCustomerType.TabIndex = 3; // // dgvProducts // this.dgvProducts.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvProducts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.colName, this.colRrp, this.colSellingPrice, this.colDiscount, this.colSavings}); this.dgvProducts.Location = new System.Drawing.Point(12, 117); this.dgvProducts.Name = "dgvProducts"; this.dgvProducts.Size = new System.Drawing.Size(561, 206); this.dgvProducts.TabIndex = 2; // // colName // this.colName.DataPropertyName = "Name"; this.colName.HeaderText = "Product Name"; this.colName.Name = "colName"; this.colName.ReadOnly = true; // // colRrp // this.colRrp.DataPropertyName = "Rrp"; this.colRrp.HeaderText = "RRP"; this.colRrp.Name = "colRrp"; this.colRrp.ReadOnly = true; // // colSellingPrice // this.colSellingPrice.DataPropertyName = "SellingPrice"; this.colSellingPrice.HeaderText = "Selling Price"; this.colSellingPrice.Name = "colSellingPrice"; this.colSellingPrice.ReadOnly = true; // // colDiscount // this.colDiscount.DataPropertyName = "Discount"; this.colDiscount.HeaderText = "Discount"; this.colDiscount.Name = "colDiscount"; // // colSavings // this.colSavings.DataPropertyName = "Savings"; this.colSavings.HeaderText = "Savings"; this.colSavings.Name = "colSavings"; this.colSavings.ReadOnly = true; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(589, 338); this.Controls.Add(this.cmbCustomerType); this.Controls.Add(this.dgvProducts); this.Name = "Form1"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox cmbCustomerType; private System.Windows.Forms.DataGridView dgvProducts; private System.Windows.Forms.DataGridViewTextBoxColumn colName; private System.Windows.Forms.DataGridViewTextBoxColumn colRrp; private System.Windows.Forms.DataGridViewTextBoxColumn colSellingPrice; private System.Windows.Forms.DataGridViewTextBoxColumn colDiscount; private System.Windows.Forms.DataGridViewTextBoxColumn colSavings;
سپس در Code Behind، با توجه به نوع UIخود یکی از روالهای زیر را انجام دهید:
در WebUI:
وارد کد نویسی صفحه Default.aspxشده و کد آن را بصورت زیر تغییر دهید:
using System; using System.Collections.Generic; using SoCPatterns.Layered.Model; using SoCPatterns.Layered.Presentation; using SoCPatterns.Layered.Service; using StructureMap; namespace SoCPatterns.Layered.WebUI { public partial class Default : System.Web.UI.Page, IProductListView { private ProductListPresenter _productListPresenter; protected void Page_Init(object sender, EventArgs e) { _productListPresenter = new ProductListPresenter(this,ObjectFactory.GetInstance<Service.ProductService>()); this.ddlCustomerType.SelectedIndexChanged += delegate { _productListPresenter.Display(); }; } protected void Page_Load(object sender, EventArgs e) { if(!Page.IsPostBack) _productListPresenter.Display(); } public void Display(IList<ProductViewModel> products) { rptProducts.DataSource = products; rptProducts.DataBind(); } public CustomerType CustomerType { get { return (CustomerType) int.Parse(ddlCustomerType.SelectedValue); } } public string ErrorMessage { set { lblErrorMessage.Text = String.Format("<p><strong>Error:</strong><br/>{0}</p>", value); } } } }
در WinUI:
وارد کدنویسی Form1شوید و کد آن را بصورت زیر تغییر دهید:
using System; using System.Collections.Generic; using System.Windows.Forms; using SoCPatterns.Layered.Model; using SoCPatterns.Layered.Presentation; using SoCPatterns.Layered.Service; using StructureMap; namespace SoCPatterns.Layered.WinUI { public partial class Form1 : Form, IProductListView { private ProductListPresenter _productListPresenter; public Form1() { InitializeComponent(); _productListPresenter = new ProductListPresenter(this, ObjectFactory.GetInstance<Service.ProductService>()); this.cmbCustomerType.SelectedIndexChanged += delegate { _productListPresenter.Display(); }; dgvProducts.AutoGenerateColumns = false; cmbCustomerType.SelectedIndex = 0; } public void Display(IList<ProductViewModel> products) { dgvProducts.DataSource = products; } public CustomerType CustomerType { get { return (CustomerType)cmbCustomerType.SelectedIndex; } } public string ErrorMessage { set { MessageBox.Show( String.Format("Error:{0}{1}", Environment.NewLine, value)); } } } }
با توجه به کد فوق، نمونه ای را از کلاس ProductListPresenter، در لحظهی نمونه سازی اولیهی کلاس UI، ایجاد نمودیم. با استفاده از متد ObjectFactory.GetInstanceمربوط به StructureMap، نمونه ای از کلاس ProductServiceایجاد شده است و به سازندهی کلاس ProductListPresenterارسال گردیده است. در مورد Structuremapدر مباحث بعدی با جزئیات بیشتری صحبت خواهم کرد. پیاده سازی معماری لایه بندی در اینجا به پایان رسید.
اما اصلا نگران نباشید، شما فقط پرواز کوتاه و مختصری را بر فراز کدهای معماری لایه بندی داشته اید که این فقط یک دید کلی را به شما در مورد این معماری داده است. این معماری هنوز جای زیادی برای کار دارد، اما در حال حاضر شما یک Applicaionبا پیوند ضعیف (Loosely Coupled)بین لایهها دارید که دارای قابلیت تست پذیری قوی، نگهداری و پشتیبانی آسان و تفکیک پذیری قدرتمند بین اجزای آن میباشد. شکل زیر تعامل بین لایهها و وظایف هر یک از آنها را نمایش میدهد.
Image may be NSFW.
Clik here to view.