TypeScript 1.6 منتشر شد
You can download TypeScript 1.6 for Visual Studio 2015, Visual Studio 2013, on npm, or as source.
آشنایی با افزونه Git Diff Margin
http://en.هشام.com/post/build-customizable-language-switcher-tag-helper-with-bootstrap
WebRequest wr = WebRequest.Create(uri); using (WebResponse response = wr.GetResponse()) { }
The remote name could not be resolved: 'en.هشام.com'
البته ممکن است کد فوق را بر روی ویندوزهای جدید بدون مشکل اجرا کنید. علت اینجا است که اگر از ویندوزهای قبل از ویندوز سرور 2012 استفاده میکنید، دات نت فریم ورک از RFC 3490 استفاده میکند؛ در غیراینصورت از RFC 5891 (فقط برای Windows 8, Windows 8.1, Windows 10, or Windows Server 2012)، با این تفاوتها.
روش رفع آن هم فعال سازی پردازش این نوع دامنهها (بر روی تمام ویندوزها) در فایلهای app.config و یا web.config به صورت زیر است:
<configuration> <uri> <idn enabled="All" /> <iriParsing enabled="true" /> </uri> </configuration>
var unicode = @"en.هشام.com"; var mapping = new IdnMapping(); // IDN = Internationalizing Domain Names var ascii = mapping.GetAscii(unicode); Console.WriteLine(ascii); string convertedBackToUnicode = mapping.GetUnicode(ascii); Console.WriteLine(convertedBackToUnicode);
کمپین ضد 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 دیگر خود را توسعه داده و استفاده کنید).
- Visual Studio Express 2013 RC for Web یا Visual Studio 2013 RC
- یک حساب کاربری در Windows Azure. میتوانید یک حساب رایگان بسازید.
یک مدیر کلی به حساب کاربری Active Directory خود اضافه کنید
- وارد Windows Azure Portal شوید.
- یک حساب کاربری (Windows Azure Active Directory (AD انتخاب یا ایجاد کنید. اگر قبلا حساب کاربری ساخته اید از همان استفاده کنید در غیر اینصورت یک حساب جدید ایجاد کنید. مشترکین Windows Azure یک AD پیش فرض با نام Default Directory خواهند داشت.
- در حساب کاربری AD خود یک کاربر جدید در نقش Global Administrator بسازید. اکانت AD خود را انتخاب کنید و Add User را کلیک کنید. برای اطلاعات کاملتر به Managing Windows Azure AD from the Windows Azure Portal 1– Sign Up with an Organizational Account مراجعه کنید.
یک نام کاربری انتخاب کرده و به مرحله بعد بروید.
نام کاربری را وارد کنید و نقش Global Administrator را به آن اختصاص دهید. مدیران کلی به یک آدرس ایمیل متناوب هم نیاز دارند. به مرحله بعد بروید.
بر روی Create کلیک کنید و کلمهی عبور موقتی را کپی کنید. پس از اولین ورود باید کلمه عبور را تغییر دهید.
یک اپلیکیشن ASP.NET بسازید
گزینه Organizational Accounts را انتخاب کنید. نام دامنه خود را وارد کنید و سپس گزینه Single Sign On, Read directory data را انتخاب کنید. به مرحله بعد بروید.
نکته: در قسمت More Options می توانید قلمرو اپلیکیشن (Application ID URI) را تنظیم کنید. تنظیمات پیش فرض برای اکثر کاربران مناسب است اما در صورت لزوم میتوانید آنها را ویرایش کنید، مثلا از طریق Windows Azure Portal دامنههای سفارشی خودتان را تنظیم کنید.
اگر گزینه Overwrite را انتخاب کنید اپلیکیشن جدیدی در Windows Azure برای شما ساخته خواهد شد. در غیر اینصورت فریم ورک سعی میکند اپلیکیشنی با شناسه یکسان پیدا کند (در پست متدهای احراز هویت در VS 2013 به تنظیمات این قسمت پرداخته شده).
اطلاعات مدیر کلی دامنه در Active Directory خود را وارد کنید (Credentials) و پروژه را با کلیک کردن بر روی Create Project بسازید.
با کلیدهای ترکیبی Ctrl + F5، اپلیکیشن را اجرا کنید. مرورگر شما باید یک اخطار SSL Certificate به شما بدهد. این بدین دلیل است که مدرک استفاده شده توسط IIS Express مورد اعتماد (trusted) نیست. این اخطار را بپذیرید و اجازه اجرا را به آن بدهید. پس از آنکه اپلیکیشن خود را روی Windows Azure منتشر کردید، این پیغام دیگر تولید نمیشود؛ چرا که Certificateهای استفاده شده trusted هستند.
با حساب کاربری سازمانی (organizational account) که ایجاد کردهاید، وارد شوید.
همانطور که مشاهده میکنید هم اکنون به سایت وارد شده اید.
توزیع اپلیکیشن روی Windows Azure
اپلیکیشن را روی Windows Azure منتشر کنید. روی پروژه کلیک راست کرده و Publish را انتخاب کنید. در مرحله تنظیمات (Settings) مشاهده میکنید که احراز هویت حسابهای سازمانی (organizational accounts) فعال است. همچنین سطح دسترسی به خواندن تنظیم شده است. در قسمت Database رشته اتصال دیتابیس را تنظیم کنید.
حال به وب سایت Windows Azure خود بروید و توسط حساب کاربری ایجاد شده وارد سایت شوید. در این مرحله دیگر نباید خطای امنیتی SSL را دریافت کنید.
خواندن اطلاعات پروفایل کاربران توسط Graph API
[Authorize] public async Task<ActionResult> UserProfile() { string tenantId = ClaimsPrincipal.Current.FindFirst(TenantSchema).Value; // Get a token for calling the Windows Azure Active Directory Graph AuthenticationContext authContext = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, LoginUrl, tenantId)); ClientCredential credential = new ClientCredential(AppPrincipalId, AppKey); AuthenticationResult assertionCredential = authContext.AcquireToken(GraphUrl, credential); string authHeader = assertionCredential.CreateAuthorizationHeader(); string requestUrl = String.Format( CultureInfo.InvariantCulture, GraphUserUrl, HttpUtility.UrlEncode(tenantId), HttpUtility.UrlEncode(User.Identity.Name)); HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); request.Headers.TryAddWithoutValidation("Authorization", authHeader); HttpResponseMessage response = await client.SendAsync(request); string responseString = await response.Content.ReadAsStringAsync(); UserProfile profile = JsonConvert.DeserializeObject<UserProfile>(responseString); return View(profile); }
کلیک کردن لینک UserProfile اطلاعات پروفایل کاربر جاری را نمایش میدهد.
اطلاعات بیشتر
public static string GetFileName(int code) { switch (code) { case 0: return "/Images/User/Weather/Tornado.png"; //... }
public class YahooWeatherRssItem { public int Code { get; set; } //... public string FileName { get { return Util.GetFileName(Code); } } }
public enum CustomerType { Person = 0, Company = 1 } public class Customer { public CustomerType Type { get; set; } }
با وجود چنین کلاسی از مشتری و نیاز به انجام فعالیتهای مختلفی بر روی آن، احتمالا نیاز خواهد بود که در بخشهای مختلف کد، گذارهی switch ای مانند زیر را اضافه کنید:
switch (customer.Type) { case CustomerType.Person: // calculate discount, or send message or edit customer or anything else break; case CustomerType.Company: // calculate discount, or send message or edit customer or anything else break; default: throw new ArgumentOutOfRangeException(); }
برای انجام فعالیتهای مختلفی مانند محاسبه تخفیف، ارسال پیام و یا ویرایش مشتری، نیاز خواهد بود این گذاره تکرار شود که خود این موضوع بوی بد duplicate code است و به الگوی shotgun surgery نیز ختم خواهد شد.
حال فرض کنید نیاز است مشتریان حقوقی، خود به دو نوع مشتری حقوقی بخش خصوصی و مشتری حقوقی بخش دولتی تقسیم شوند. در پیاده سازی ذکر شده باید به CustomerType یک آیتم افزوده شود و در تمامی switchها نیز در صورت نیاز شرط مربوط به آن اضافه شود.
برای حل این نوع از کد بد بو، معمولا یک کلاس پدر را به نام مشتری ایجاد کرده و کلاسهای مختص هر یک از انواع مشتری را از آن به ارث میبرند (Replace type code with subclass):
یا میتوان طراحی را کمی متفاوتتر و به صورت زیر انجام داد:
دلیل مشابه دیگر ایجاد این الگوی بد کد استفاده از type code به عنوان وضعیت یک تایپ است. که در این صورت میتوان بجای type code از state object استفاده کرد (Replace type code with strategy). به این مورد در مباحث مربوط به refactoring به طور مفصل پرداخته شده است.
جمع بندی
این کد بد بو در شرایط متفاوتی ایجاد میشود. با این حال یکی از پر تکرارترین آنها استفاده بد یا عدم استفاده از الگوهای طراحی شیء گرا است. تصحیص این الگوی بد، به خوانایی و نگهداری کد در بلند مدت کمک بسیار زیادی میکند.