نظرات مطالب
رمزنگاری کانکشن استرینگ در ASP.Net
@ نیما
- در وب برای اینکه این کاراکترهای عجیب و غریب مشکل ساز نشوند یکبار دیگر هم اطلاعات رمزنگاری شده را از فیلتر base64 encoding عبور می‌دهند. به این صورت مشکلی برای نگهداری آن‌ها در فایل‌ها وجود نخواهد داشت.
- نگهداری اطلاعات حساس در حافظه به صورت plain کار اشتباهی است چون دامپ حافظه ویندوز و یا تمام سیستم‌ عامل‌های دیگر کار ساده‌ای است. برنامه‌های زیادی هستند که پروسس‌های ویندوز را لیست می‌کنند و به شما اجازه می‌دهند حافظه آن‌ها را دامپ کنید (به راحتی چند کلیک). استخراج اطلاعات حساس هم از یک فایل دامپ تر و تازه زیاد مشکل نیست.
- سرعت الگوریتم‌های رمزنگاری واقعا بالا است. پیاده سازی‌های خیلی خوبی هم دارند. بنابراین زیاد نگران این سربار نباشید. چون در حد یک رشته ساده و امثال آن اصلا سرباری به حساب نمی‌آیند و بسیار سریع عمل می‌کنند.
نظرات مطالب
اعتبارسنجی سرویس های WCF
سلام .ممنون به خاطر زحماتتون.
بر طبق آموزشهای گوناگون برای اعمال امنیت روی سرویس میتوان از Certificate  هایی استفاده کرد که خودمان آنها را تولید کرده ایم. البته سفارش شده که  در زمان برنامه نویسی و پیاده سازی پروژه از آن استفاده شود نه برای زمان واقعی استفاده از سرویس.
آیا  این امکان وجود دارد که از Certificate هایی که خودمان ایجاد کرده ایم  در پروژه‌های واقععی استفاده  کنیم؟
اگر این امکان وجود دارد  آیا این Certificate ‌ها کار رمز گزاری و  رمز گشایی را برای ما انجام میدهند؟ و چه محدودیتهایی دارند؟
با تشکر؟
نظرات مطالب
یکپارچه سازی Angular CLI و ASP.NET Core در VS 2017
روندی که برای اجرای کامندهای CLI باید صادر کرد رو بیشتر منظورم هست همونطور که میدونید وقتی کامند ایجاد رو میزنیم خود CLI پوشه src رو میسازه و بیرون از اون یه سری فایل دیگه، با توجه به ساختار پروژه‌ها در VS کامل و در نظر نگرفتن فایلهای بیرون از پوشه پروژه توسط VS این کار چطور باید انجام بشه؟
آیا کامند رو روی یک فولدر تمپ برای استخراج پوشه src میزنیم و محتویات اون رو به سلوشن منتقل میکنیم(در مواردی که سرچ کردم اینو پیشنهاد کرده بودند) و یا روش درست و استانداردی برای ون وجود داره؟
بازخوردهای پروژه‌ها
توضیحات
۱) آیا فایل راهنما برای متدهای باشگاه مشتریان موجود هست؟ (بنده در مستندات، هیچ توضیحی در مورد این متد پیدا نکردم)
۲) آیا در قسمت additionaldata ، فرمت داده باید دقیقا حالتی که شما اینجا مثال زدید باشد یا اینکه حالات دیگری هم امکان پذیر هست؟
۳) متد دیگری هم به نام bpDynamicPayRequest موجود هست. فرق این دو متد چیست؟
1: مستندات پروژه:
2: بله براساس ساختار ارسال باشد باشد که شامل 2 بخش می‌شود
بخش اول شامل mechentId باشگاه مشتریان هست و مبلغ درصد آنها
بخش دوم شامل merchentId کاربر بعد از چند حساب شدن و مبلغ درصد کاربر
3: براساس مستنداتی برای من ارسال شده فقط این متد استفاده شده بود

نظرات مطالب
AngularJS #2
با سلام سوالی داشتم در بالا فرمودید: "من در پروژه خودم یک کنترلر تعریف کردم که در آن همه‌ی actionها فقط partial view برمیگرداند" یعنی چی؟  
"حال قبل از اینکه این فایل‌های cshtml  تبدیل به html شوند وبه کلاینت برگردانده شوند" این جمله هم نامفهوم بود. ممنون میشم اگر توضیح بیشتری بدید.
مطالب
ایجاد یک DbContext مشترک بین entityهای پروژه‌های متفاوت
فر ض کنید پروژه بزرگی دارید که هر قسمت را به یک برنامه نویس می‌سپارید تا آن قسمت را در پروژه مجزایی طراحی و برنامه نویسی کند. هر برنامه نویس Entity‌های خاص خود را در لایه‌های مربوط به پروژه خود تعریف می‌کند و از آنها استفاده می‌کند. حال یکی از برنامه نویس‌ها می‌خواهد از Entity های پروژه دیگر استفاده کند. در این صورت اگر از دو Context شیء‌ایی را بسازد و آنها را با یکدیگر Join  بزند، خطایی مربوط به تعلق داشتن دو  Entity به دو Context متفاوت را می‌گیرد.

در پروژه‌های کوچک، کل تیم بر روی ماژول‌های مختلف یک پروژه کار می‌کنند و یک DbContext مشترک دارند. اما راه حل این مشکل در پروژه‌های بزرگ چیست؟ 
یکی از راه‌های پیشنهادی، استفاده از یک کلاس DbContextBase است که همه پروژه‌ها بایستی Context خود را از این کلاس به ارث ببرند که در این صورت باز هم مشکل ساخت چند DbContext وجود خواهد داشت که فقط می‌توان از Entity‌های موجود در DbContextBase و DbContext پروژه جاری استفاده کرد. اما در شرکت‌های بزرگ که پروژه‌هایی مانندERP دارند، روش دیگری استفاده می‌شود که در ادامه خواهیم دید.
روش مورد استفاده به این صورت است که در زمان اجرا یک DbContext برای همه Entity‌های پروژه‌های مختلف ساخته می‌شود. اجازه بدهید همراه با مثال، این پروژه را پیش برویم. فرض کنید دو تیم برنامه نویسی داریم که هر کدام بر روی پروژه‌های مجزای SampleProject1 و SampleProject2 کار میکنند که Entity‌های هر کدام در لایه‌های Common قرار گرفته‌اند.

در SampleProject1 مدل Product را داریم:

public partial class Product : Entity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Nullable<byte> ProductTypeId { get; set; }
    }
و در SampleProject2، مدل ProductType را داریم که هر دو Entity از کلاس Entity ارث بری می‌کند: 
 public partial class ProductType : Entity
    {
        public byte Id { get; set; }
        public string Name { get; set; }
    }
همه پروژه‌ها را در پروژه‌ی SampleProject1.Console، به عنوان رفرنس اضافه می‌کنیم؛ بجز SampleProject2.Console و Output path همه پروژه‌ها را به یک پوشه مشترک هدایت می‌کنیم. در ادامه برای بدست آوردن Entity‌ها از کد زیر استفاده می‌کنیم:
            List<Assembly> allAssemblies = new List<Assembly>();
            string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            foreach (string dll in Directory.GetFiles(path, "*.Common.dll"))
                allAssemblies.Add(Assembly.LoadFile(dll));

            var type = typeof(Entity);
      
            List<Type> types = allAssemblies
             .SelectMany(s => s.GetTypes())
             .Where(p => type.IsAssignableFrom(p)).ToList();

            List<string> entities = new List<string>();
            foreach (var item in types)
            {
                entities.Add(item.Name);
            }

            types.Add(typeof(Entity));
و سپس برای Generate کردن کلاس DbContext از کلاس زیر استفاده می‌کنیم:
public class ContextGenerator
    {
        public void Generate(List<string> entities, params Type[] types)
        {
            StringBuilder code = new StringBuilder();

            code.AppendLine(@"
           using System.Data.Entity;
           using System.Data.Entity.Core.EntityClient;
           using SampleProject1.Common.Models;
           using SampleProject1.Common.Models.Mapping;
           using SampleProject2.Common.Models;
           using SampleProject2.Common.Models.Mapping;

           namespace DbContextGenerator
           {
                public partial class TestContext : DbContext
                {
                    static TestContext()
                    {
                        Database.SetInitializer<TestContext>(null);
                    }

                    public TestContext()
                        : base(""Data Source=.;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True"")
                    {
                        }
                ");

            var pluralizeHelper = new PluralizeHelper();

            foreach (var entity in entities)
            {
                code.AppendLine($@"public DbSet<{entity}> {pluralizeHelper.Pluralize(entity)} {{ get; set; }}");
            }

            code.AppendLine(@"protected override void OnModelCreating(DbModelBuilder modelBuilder)");
            code.AppendLine(@"{");

            foreach (var entity in entities)
            {
                code.AppendLine($@"modelBuilder.Configurations.Add(new {entity}Map());");
            }
            code.AppendLine(@"}");
            code.AppendLine(@"}");
            code.AppendLine(@"}");
           
            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters();

            parameters.ReferencedAssemblies.Add("System.Drawing.dll");
            parameters.ReferencedAssemblies.Add("System.Data.dll");
            parameters.ReferencedAssemblies.Add("System.Data.Entity.dll");
            parameters.ReferencedAssemblies.Add("System.ComponentModel.dll");

            foreach (var type in types)
            {
                parameters.ReferencedAssemblies.Add(type.Assembly.Location);
            }

            parameters.ReferencedAssemblies.Add(typeof(DbSet).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(DbContext).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(IQueryable).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(IQueryable<>).Assembly.Location);
            parameters.ReferencedAssemblies.Add(typeof(System.ComponentModel.IListSource).Assembly.Location);

            parameters.GenerateExecutable = false;
            parameters.GenerateInMemory = false;
            parameters.OutputAssembly = "ProjectContext.dll";

            CompilerResults results = provider.CompileAssemblyFromSource(parameters, code.ToString());

            if (results.Errors.HasErrors)
            {
                StringBuilder sb = new StringBuilder();

                foreach (CompilerError error in results.Errors)
                {
                    sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
                }

                throw new InvalidOperationException(sb.ToString());
            }
        }

    }
و نحوه فراخوانی آن:
 new ContextGenerator().Generate(entities, types.ToArray()); // generate dbContext
همانطور که مشاهده می‌کنید، برای تولید کد، از کلاس CSharpCodeProvider استفاده میکنیم که نتیجه اجرای کد بالا، ساخت DLLی به نام ProjectContext.dll است. با مشاهده DLL ساخته شده توسط نرم افزار ILSpy، کد جنریت شده به صورت زیر خواهد بود: 

حال برای استفاده از Context تولید شده، به صورت زیر شیءایی را ساخته:

 static DbContext _dbContext=null;
        public static DbContext GetDbContextInstance()
        {
            if (_dbContext == null)
            {
                string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
                var dllversionAssm = Assembly.LoadFile(path + "\\ProjectContext.dll");
                Type type = dllversionAssm.GetType("DbContextGenerator.TestContext");
                _dbContext = (DbContext)Activator.CreateInstance(type);
            }
            return _dbContext;
        }

و سپس برای ساخت DbSet از هر Entity به کد زیر نیاز خواهیم داشت:

public static System.Data.Entity.DbSet<T> Get<T>() where T : class
        {
            var set = GetDbContextInstance().Set<T>();
            return set;
        }

هم اکنون می‌توان رکوردهای Entity‌ها را واکشی کرده و یا آن‌ها را با یکدیگر Join بزنیم:

            var products = Get<Product>().ToList();

            var productTypes = Get<ProductType>().ToList();


            var query = from p in Get<Product>()
                        join pt in Get<ProductType>() on p.ProductTypeId equals pt.Id
                        select new
                        {
                            Id = p.Id,
                            Name = p.Name,
                            ProductType = pt.Name

                        };

            var JoinResult = query.ToList();

و نتیجه واکشی ها 


کد کامل این پروژه  

مطالب
Virtual Scrolling در Angular 7
یکی از امکانات Angular 7، ویژگی Virtual Scrolling می‌باشد. در صورتیکه شما قصد داشته باشید یک لیست بزرگ از المنت‌ها را  بارگذاری کنید، این‌کار می‌تواند بر روی کارآیی برنامه‌ی شما تاثیر بگذارد . تگ زیر
<cdk-virtual-scroll-viewport></cdk-virtual-scroll-viewport>
می تواند برای بارگذاری تنها بخش‌های قابل مشاهده‌ی از یک لیست، بر روی صفحه نمایش استفاده شود و همچنین تنها آیتم‌هایی Render خواهند شد که می‌تواند آن‌ها را در صفحه نمایش جا دهد. اگر لیست بارگذاری شده را اسکرول کنیم، در این حالت المنت‌ها در DOM  به صورت پویا  load و unload می‌شوند. 
قبل از پیاده سازی ، لازم است Angular CLI  را به آخرین نسخه بروز رسانی کنیم. برای بروز رسانی Angular CLI  دستور زیر را اجرا می‌کنیم:
npm install -g @angular/cli
بعد از نصب با استفاده از ng version  نسخه‌ی Angular CLI  را بررسی می‌کنیم که باید بزرگتر از 7 باشد:

حالا نوبت به ایجاد یک پروژه‌ی جدید می‌باشد. با استفاده از دستور زیر یک پروژه جدید ایجاد می‌شود:
ng new angular7-virtualScrolling
بعد از تایید دستور بالا،  دو سؤال از شما پرسیده می‌شود؟
1- آیا قصد دارید Angular routing اضافه شود یا نه؟ ( در نسخه‌های قبلی با استفاده از routing--  این کار را انجام می‌دادیم)


2-انتخاب فرمت stylesheet که قصد استفاده‌ی از آن‌را دارید ( با کلید‌های جهتی بالا و پایین روی صحفه کلید می‌توانید یکی از گزینه‌ها را انتخاب کنید )


برای استفاده از Virtual Scrolling نیاز است پکیج زیر را نصب کنیم : 
npm install @angular/cdk@latest
بعد از نصب، دستور ng serve را اجرا می‌کنیم تا بررسی کنیم که برنامه به درستی اجرا می‌شود یا نه. سپس فایل app.module.ts را باز می‌کنیم و ScrollingModule را در بخش imports اضافه می‌کنیم. اکنون نیاز است تا یک آرایه را برای نمایش آیتم‌های لیست، تولید کنیم. قطعه کد زیر در فایل app.component.ts  قرار دارد که یک آرایه عددی را ایجاد می‌کند و تعدادی آیتم را به آن اضافه می‌کند:
  title = 'Angular 7 – Virtual Scrolling feature';
  scrollItems: number[] = [];
  constructor() {
    for (let index = 0; index < 10000; index++) {
      this.scrollItems.push(index);
    }
  }

در فایل app.component.html  قطعه کد زیر را قرار می‌دهیم:
    <div>
      <h4>
        {{this.title}}
      </h4>
      <cdk-virtual-scroll-viewport itemSize="100">
        <div *cdkVirtualFor="let n of scrollItems">Item {{n}}</div>
      </cdk-virtual-scroll-viewport>
    </div>

داخل تگ  cdk-virtual-scroll-viewport، یک div را ایجاد و سپس یک دایرکتیو را به نام cdkVirtualFor* به آن اضافه می‌کنیم. این دایرکتیو، ngFor* را درون cdk-virtual-scroll-viewport، جایگزین می‌کند که شما با استفاده از آن می‌توانید یک حلقه بر روی آرایه  scrollItems  جهت پیمایش ایجاد کنید.
تمام ! اکنون پروژه را اجرا کنید.
در اولین بار اجرا :    


بعد از اسکرول کردن لیست : 


همانطور که مشاهده می‌کنیم المنت‌های قبلی unload شدند و المنت‌های جدید load شدند
DEMO 


مطالب
اصول طراحی شیء گرا: OO Design Principles - قسمت چهارم

همانطور که قول داده بودم، به اصول GRASP می‌پردازیم.

اصول GRASP-General Responsibility Assignment Software Principles

این اصول به بررسی نحوه تقسیم وظایف بین کلاس‌ها و مشارکت اشیاء برای به انجام رساندن یک مسئولیت می‌پردازند. اینکه هر کلاس در ساختار نرم افزار چه وظیفه‌ای دارد و چگونه با کلاس‌های دیگر مشارکت میکند تا یک عملکرد به سیستم اضافه گردد. این اصول به چند بخش تقسیم می­شوند:

  • کنترلر ( Controller )
  • ایجاد کننده ( Creator )
  • انسجام قوی ( High Cohesion )
  • واسطه گری ( Indirection )
  • دانای اطلاعات ( Information Expert )
  • اتصال ضعیف ( Low Coupling )
  • چند ریختی ( Polymorphism )
  • حفاظت از تاثیر تغییرات ( Protected Variations )
  • مصنوع خالص ( Pure Fabrication )

 

Controller

این الگو بیان می‌کند که مسئولیت پاسخ به رویداد‌های (Events ) یک سناریوی محدود مانند یک مورد کاربردی ( Use Case ) باید به عهده یک کلاس غیر UI باشد. کنترلر باید کارهایی را که نیاز است در پاسخ رویداد انجام شود، به دیگران بسپرد و نتایج را طبق درخواست رویداد بازگرداند. در اصل، کنترلر دریافت کننده رویداد، راهنمای مسیر پردازش برای پاسخ به رویداد و در نهایت برگرداننده پاسخ به سمت مبداء رویداد است. در زیر مثالی را می‌بینیم که رویداد اتفاق افتاده توسط واسط گرافیکی به سمت یک handler (که متدی است با ورودیِ فرستنده و آرگمانهای مورد نیاز) در کنترلر فرستاده میشود. این روش event handling، در نمونه‌های وب فرم و ویندوز فرم دیده میشود. به صورتی خود کلاس‌های .Net وظیفه Event Raising از سمت UI با کلیک روی دکمه را انجام میدهد: 

 public class UserController
 {        
        protected void OnClickCreate(object sender, EventArgs e)
        {
           // call validation services
           // call create user services
        }
 }


در مثال بعد عملیات مربوط به User در یک WebApiController پاسخ داده میشود. در اینجا به جای استفاده از Event Raising برای کنترل کردن رویداد، از فراخوانی یک متد در کنترلر توسط درخواست HttpPost انجام میگیرد. در اینجا نیاز است که در سمت کلاینت درخواستی را ارسال کنیم:

    public class UserWebApiController
    {
        [HttpPost]
        public HttpResponseMessage Create(UserViewModel user)
        {
            // call validation services
            // call create user services
        }
    }



Creator :

  این اصل میگوید شیء ای میتواند یک شیء دیگر را بسازد ( instantiate ) که: (اگر کلاس B بخواهد کلاس A را instantiate کند)

  • کلاس B شیء از کلاس A را در خود داشته باشد؛
  • یا اطلاعات کافی برای instantiate کردن از A را داشته باشد؛
  • یا به صورت نزدیک با A در ارتباط باشد؛
  • یا بخواهد شیء A را ذخیره کند.

از آنجایی که این اصل بدیهی به نظر میرسد، با مثال نقض، درک بهتری را نسبت به آن میتوان پیدا کرد:

    // سازنده
    public class B
    {
        public static A CreateA(string name, string lastName, string job)
        {
            return new A() {
                Name =name,
                LastName = lastName,
                Job = job
            };
        }
    }
    // ایجاد شونده
    public class A
    {
        public string Name { get; set; }
        public string LastName { get; set; }
        public string Job { get; set; }
    }

    public class Context
    {
        public void Main()
        {
            var name = "Rasoul";
            var lastName = "Abbasi";
            var job = "Developer";            
            var obj = B.CreateA(name, lastName, job);
        }
    }


و اما چرا این مثال، اصل Creator را نقض میکند. در مثال میبینید که کلاس B، یک شیء از نوع A را در متد Main کلاس Context ایجاد میکند. کلاس B فقط یک متد برای تولید A دارد و در عملیات تولید A هیچ منطق خاصی را پیاده سازی نمیکند.کلاس B شیء ای را از کلاس A ، در خود ندارد، با آن ارتباط نزدیک ندارد و آنرا ذخیره نمیکند. با اینکه کلاس B اطلاعات کافی را برای تولید A از ورودی میگیرد، ولی این کلاس Context است که اطلاعات کافی را ارسال مینماید. اگر در کلاس B منطقی اضافه بر instance گیریِ ساده وجود داشت (مانند بررسی صحت و اعتبار سنجی)، میتوانستیم بگوییم کلاس B از یک مجموعه عملیات instance گیری با خبر است که کلاس Context  نباید از آن خبر داشته باشد. لذا اکنون هیچ دلیلی وجود ندارد که وظیفه تولید A را در Context انجام ندهیم و این مسئولیت را به کلاس B منتقل کنیم. این مورد ممکن است در ذهن شما با الگوی Factory تناقض داشته باشد. ولی نکته اصلی در الگو Factory انجام عملیات instance گیری با توجه به منطق برنامه است؛ یعنی وظیفه‌ای که کلاس Context نباید از آن خبر داشته باشد را به کلاس Factory منتقل میکنیم. در غیر اینصورت ایجاد کلاس Factory بی معنا خواهد بود (مگر به عنوان افزایش انعطاف پذیری معماری که بتوان به راحتی نوع پیاده سازی یک واسط را تغییر داد).


High Cohesion :

این اصل اشاره به یکی از اصول اساسی طراحی نرم افزار دارد. انسجام واحد‌های نرم افزاری باعث افزایش خوانایی، سهولت اشکال زدایی، قابلیت نگهداری و کاهش تاثیر زنجیره‌ای تغییرات میشود. طبق این اصل، مسئولیتهای هر واحد باید مرتبط باشد. لذا اجزایی کوچک با مسئولیتهای منسجم و متمرکز بهتر از اجزایی بزرگ با مسئولیت‌های پراکنده است. اگر واحد‌های سازنده نرم افزار انسجام ضعیفی داشته باشند، درک همکاری‌ها، استفاده مجدد آنها، نگه داری نرم افزار و پاسخ به تغییرات سخت‌تر خواهد شد.

در مثال زیر نقض این اصل را مشاهده میکنیم:

    class Controller
    {
        public void CreateProduct(string name, int categoryId) { }
        public void EditProduct(int id, string name) { }
        public void DeleteProduct(int id) { }
        public void CreateCategory(string name) { }
        public void EditCategory(int id, string name) { }
        public void DeleteCategory(int id) { }
    }  

همانطور که میبینید، کلاس کنترلر ما، مسئولیت مدیریت Product و Category را بر عهده دارد. بزرگ شدن این کلاس، باعث سخت‌تر شدن خواندن کد و رفع اشکال میگردد. با جداسازی کنترلر مربوط به Product از Category میتوان انسجام را بالا برد.


Indirection :

 این اصل بیان میکند که با تعریف یک واسط بین دو مولفه نرم افزاری میتوان میزان اتصال نرم افزار را کاهش داد. بدین ترتیب وظیفه هماهنگی ارتباط دو مؤلفه، به عهده این واسط خواهد بود و نیازی نیست داده‌های ورودی و خروجی دو مؤلفه، هماهنگ باشند. در اینجا واسط، از وابستگی بین دو مؤلفه با پنهان کردن ضوابط هر مؤلفه از دیگری و ایجاد وابستگی ضعیف خود با دو مؤلفه، باعث کاهش اتصال کلی طراحی میگردد.

الگوهای Adapter و Delegate و همچنین نقش کنترلر در الگوی معماری MVC از این اصل پیروی میکنند. 

    class SenderA
    {
        public Mediator mediator { get; }
        public SenderA() { mediator = new Mediator(); }
        public void Send(string message, string reciever) { mediator.Send(message, reciever); }
    }
    class SenderB
    {
        public Mediator mediator { get; }
        public SenderB() { mediator = new Mediator(); }
        public void Send(string message) { }
    }

    public class RecieverA
    {
        public void DoAction(string message)
        {
            // انجام عملیات بر اساس پیغام دریافت شده
            switch (message)
            {
                case "create":
                    break;
                case "delete":
                    break;
                default:
                    break;
            }
        }
    }
    public class RecieverB
    {
        public void DoAction(string message)
        {
            // انجام عملیات بر اساس پیغام دریافت شده
            switch (message)
            {
                case "edit":
                    break;
                case "rollback":
                    break;
                default:
                    break;
            }
        }
    }
    class Mediator
    {
        internal void Send(string message, string reciever)
        {
            switch (reciever)
            {
                case "A":
                    var recieverObjA = new RecieverA();
                    recieverObjA.DoAction(message);
                    break;
                case "B":
                    var recieverObjB = new RecieverB();
                    recieverObjB.DoAction(message);
                    break;

                default:
                    break;
            }
        }
    }
    class IndirectionContext
    {
        public void Main()
        {
            var senderA = new SenderA();
            senderA.Send("rollback", "B");
            var senderB = new SenderA();
            senderB.Send("create", "A");

        }
    }

در این مثال کلاس Mediator به عنوان واسط ارتباطی بین کلاس‌های Sender و Receiver قرار گرفته و نقش تحویل پیغام را دارد.

در مقاله بعدی، به بررسی سایر اصول GRASP خواهم پرداخت.

مطالب
چگونه کد قابل تست بنویسیم - قسمت اول
مقدمه 
نوشتن تست برای کدها بسیار عالی است، در صورتیکه بدانید چگونه این کار را بدرستی انجام دهید. متأسفانه بسیاری از منابع آموزشی موجود، این مطلب که چگونه کد قابل تست بنویسیم را رها می‌کنند؛ بدلیل اینکه آنها مراقبند در بین لایه هایی که در کدهای واقعی وجود دارند گیر نکنند، جایی که شما لایه‌های خدمات (Service Layer)، لایه‌های داده، و غیره را دارید. به ضرورت، وقتی میخواهید کدی را تست کنید که این وابستگی‌ها را دارد، تست‌ها بسیار کند و برای نوشتن دشوار هستند و اغلب بدلیل وابستگی‌ها شکست میخورند و نتیجه غیر قابل انتظاری خواهند داشت.

پیش زمینه
کدی که به خوبی نوشته شده باشد از لایه‌های جداگانه ای تشکیل شده است که هر لایه مسئول یک قسمت متفاوت از وظایف برنامه خواهد بود. لایه‌های واقعی بر اساس نیاز و نظر توسعه دهندگان متفاوت است، ولی یک ساختار رایج به شکل زیر خواهد بود.

لایه نمایش / رابط کاربری : این قسمت کد منطق نمایش و تعامل رابط کاربری می‌باشد.
منطق تجاری / لایه خدمات : این قسمت منطق تجاری کد شما میباشد. برای نمونه در کد مربوط به یک کارت خرید، این کارت خرید میداند چگونه جمع کارت را محاسبه نماید و یا چگونه اقلام موجود در سفارش را شمارش کند.
لایه دستیابی به داده / لایه ماندگاری :  این کد میداند چگونه به منبع داده متصل شود و یک کارت خرید را بازگرداند و یا چگونه یک کارت را در منبع داده ذخیره نماید.
منبع داده : اینجا جایی است که محتویات کارت در آن ذخیره میشود.

مزیت مدیریت وابستگی‌ها 

بدون مدیریت وابستگی‌ها، وقتی شما برای لایه نمایش تست می‌نویسید، کد شما در خدمات واقعی که به کد واقعی دستیابی به منبع داده وابسته است، گرفتار می‌شود و سپس به منبع داده اصلی متصل می‌شود. در واقع، وقتی شما در حال تست نویسی برای گزینه "اضافه به کارت" و یا "دریافت تعداد اقلام" هستید، می‌خواهید کد را به صورت مجزا تست کنید و قادر باشید نتایج قابل پیش بینی را تضمین نمایید. بدون مدیریت وابستگی‌ها، تست‌های نمایش شما برای گزینه "اضافه به کارت" کند هستند و وابستگی‌ها نتایج غیر قابل پیش بینی بازمیگردانند که میتوانند باعث پاس نشدن تست شما شوند.

راه حل تزریق وابستگی است
راه حل این مسأله تزریق وابستگی است. تزریق وابستگی برای کسانی که تا بحال از آن استفاده نکرده اند، اغلب گیج کننده و پیچیده به نظر میرسد، اما در واقع، مفهومی بسیار ساده و فرآیندی با چند اصل ساده است. آنچه میخواهیم انجام دهیم مرکزیت دادن به وابستگی هاست. در این مورد، استفاده از شیء کارت خرید است و سپس رابطه بین کدها را کم می‌کنیم تا جاییکه وقتی شما برنامه را اجرا می‌کنید، از خدمات و منابع واقعی استفاده کند و وقتی آنرا تست می‌کنید، می‌توانید از خدمات جعلی (mocking) استفاده نمایید که سریع و قابل پیش بینی هستند. توجه داشته باشید که رویکردهای متفاوت بسیاری وجود دارند که میتوانید استفاده کنید، ولی من برای ساده نگهداشتن این مطلب، فقط رویکرد Constructor Injection را شرح میدهم.

گام 1 -  وابستگی‌ها را شناسایی کنید
وابستگی‌ها وقتی اتفاق می‌افتند که کد شما از لایه‌های دیگر  استفاده می‌نماید. برای نمونه، وقتی لایه نمایش از لایه خدمات استفاده می‌نماید. کد نمایش شما به لایه خدمات وابسته است، ولی ما میخواهیم کد لایه نمایش را به صورت مجزا تست کنیم.
public class ShoppingCartController : Controller
{
    public ActionResult GetCart()
    {
        //shopping cart service as a concrete dependency
        ShoppingCartService shoppingCartService = new ShoppingCartService();
        ShoppingCart cart = shoppingCartService.GetContents();
        return View("Cart", cart);
    }
    public ActionResult AddItemToCart(int itemId, int quantity)
    {
        //shopping cart service as a concrete dependency
        ShoppingCartService shoppingCartService = new ShoppingCartService();
        ShoppingCart cart = shoppingCartService.AddItemToCart(itemId, quantity);
        return View("Cart", cart);
    }
}

 
گام 2 – وابستگی‌ها را مرکزیت دهید
این کار با چندین روش قابل انجام است؛ در این مثال من میخواهم یک متغیر عضو از نوع ShoppingCartService ایجاد کنم و سپس آنرا به وهله ای که در Constructor ایجاد خواهم کرد، منتسب کنم. حال هرجا ShoppingCartService نیاز باشد بجای آنکه یک وهله جدید ایجاد کنم، از این وهله استفاده می‌نمایم. 
public class ShoppingCartController : Controller
{
    private ShoppingCartService _shoppingCartService;
    public ShoppingCartController()
    {
        _shoppingCartService = new ShoppingCartService();
    }
    public ActionResult GetCart()
    {
        //now using the shared instance of the shoppingCartService dependency
        ShoppingCart cart = _shoppingCartService.GetContents();
        return View("Cart", cart);
    }
    public ActionResult AddItemToCart(int itemId, int quantity)
    {
        //now using the shared instance of the shoppingCartService dependency
        ShoppingCart cart = _shoppingCartService.AddItemToCart(itemId, quantity);
        return View("Cart", cart);
    }
}

 در قسمت بعد درباره مواردی مانند از بین بردن ارتباط لایه ها، تزریق وابستگی و نوشتن تست بحث خواهم کرد.