مطالب
بررسی Bad code smell ها: میراث رد شده

میراث رد شده یا Refused bequest به دسته «بد استفاده کنندگان از شیء گرایی» تعلق دارد. این دسته از کدهای بد بو، معمولا استفاده ناقص یا نادرستی از مفاهیم و اصول شیء گرایی دارند. 
زمانیکه یک کلاس تنها بخشی از اعضای (خصوصیت، متد و ...) کلاس پدر خود را استفاده می‌کند، با این الگو سر و کار داریم. در چنین شرایطی دیگر اعضای کلاس پدر یا استفاده نمی‌شوند و یا حتی در صورت پیاده سازی شدن توسط کلاس، بلااستفاده می‌مانند. به طور مثال متدهایی از کلاس پدر پیاده سازی می‌شوند و با پرتاب یک استثناء در بدنه‌شان از کار می‌افتند. 
یکی از دلایل مهم ایجاد چنین کدهای بد بویی، ایجاد رابطه ارث بری تنها برای استفاده دوباره از کدهای یک کلاس است. در صورتیکه ممکن است کلاس پدر و فرزند هیچ ارتباط منطقی ای از نظر ارث بری با یکدیگر نداشته باشند.   
به طور مثال فرض کنید در حال توسعه یک محصول هستید که در آن فروش کالا اتفاق می‌افتد. در ابتدای کار، تنها کاربران این محصول، شرکت‌ها هستند. روالی نیز برای محاسبه تخفیف مربوط به شرکت‌ها ایجاد شده است. در این روال یک تخفیف پایه وجود دارد که به همه مشتریان تعلق می‌گیرید و تخفیف‌هایی نیز وجود دارند که به هر یک از شرکت‌ها تعلق می‌گیرند. میزان تخفیف نهایی برای یک شرکت از مجموع این دو مقدار بدست می‌آید. کلاس مربوط به محاسبه تخفیف به این صورت است: 

public class DiscountCalculator 
{ 
    protected decimal CalculateGeneralDiscount() 
    { 
        // calculate general discount 
        return 0; 
    } 
    protected decimal AddSpecificDiscount(decimal baseDiscount) 
    { 
        // add specific discounts to base discount 
        return 0; 
    } 
    protected virtual decimal GetFinalDiscount() 
    { 
        var baseDiscount = CalculateGeneralDiscount(); 
        var addedDiscount = AddSpecificDiscount(baseDiscount); 
        return addedDiscount; 
    } 
}
بعد از مدتی نیاز می‌شود که افراد حقیقی از نرم افزار استفاده کرده و خرید نمایند و این مکانیزم تخفیف برای آن‌ها نیز اعمال شود. توسعه دهنده بعد از بررسی به این نتیجه می‌رسد که تنها تفاوت این مکانیزم با مکانیزم قبلی، متد AddSpecificDiscount است که باید برای اشخاص حقیقی بازنویسی شود. این توسعه دهنده تصمیم می‌گیرد برای استفاده دوباره از کد موجود در کلاس DiscountCalculator، کلاس مربوط به اشخاص حقیقی را از آن ارث ببرد. که نتیجه به صورت زیر خواهد بود:
public class PersonDiscountCalculator : DiscountCalculator 
{ 
    private decimal AddSpecificDiscountForPerson(decimal baseDiscount) 
    { 
        // calculate base discount for person 
        return 0; 
    } 
    protected override decimal GetFinalDiscount() 
    { 
        var baseDiscount = CalculateGeneralDiscount(); 
        var added = AddSpecificDiscountForPerson(baseDiscount); 
        return added; 
    } 
}

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

روش‌های اصلاح این نوع کد بد بو 

1) زمانیکه رابطه ارث بری هیچ معنایی ندارد، می‌توان به جای استفاده از ارث بری، شیء مربوط به کلاس پدر را ایجاد و در بدنه کلاس فرزند از آن استفاده کرد. مثلا برای فراخوانی متدها یا دسترسی به خصوصیت‌ها (مثال ذکر شده در این دسته نیست).
2) زمانیکه رابطه منطقی وجود داشته باشد، ولی اعضای اضافی کلاس پدر، به فرزند به ارث برسند، راه حل معمولا در جدا کردن کلاس پدر و ایجاد یک کلاس جدید پدر برای مصرف مورد نظر است.  
در مثال مطرح شده رابطه منطقی بین کلاس‌ها وجود دارد؛ ولی نه به شکلی که پیاده سازی شده است. یکی از راه‌های مناسب برای رفع چنین مشکلی، ایجاد یک کلاس پدر مشترک (در اینجا DiscountCalculator) و انتقال منطق‌های مربوطه به آن و ارث بری کردن تک تک کلاسها از آن است.  
public abstract class DiscountCalculator 
{ 
    protected decimal CalculateGeneralDiscount() 
    { 
        // calculate general discount 
        return 0; 
    } 
    protected abstract decimal AddSpecificDiscount(decimal baseDiscount); 
    protected virtual decimal GetFinalDiscount() 
    { 
        var baseDiscount = CalculateGeneralDiscount(); 
        var addedDiscount = AddSpecificDiscount(baseDiscount); 
        return addedDiscount; 
    } 
}

کلاس DiscountCalculator به عنوان کلاس پایه برای محاسبات تخفیف تعریف شده و کلاس‌های مربوط به شرکت و شخص از آن ارث بری می‌کنند. به این صورت که محاسبات کلی تخفیف‌ها در کلاس پایه و محاسبات مربوط به افزودن تخفیف‌های خاص در کلاس‌های فرزند انجام می‌شود (با فرض این که محاسبات مربوط به افزودن تخفیف‌های خاص تفاوت دارند).   
public class CompanyDiscountCalculator: DiscountCalculator 
{ 
    protected override decimal AddSpecificDiscount(decimal baseDiscount) 
        { 
            // add some customer specific discounts to base discount 
            return 0; 
        } 
    } 
    public class PersonDiscountCalculator : DiscountCalculator 
    { 
        protected override decimal AddSpecificDiscount(decimal baseDiscount) 
        { 
            // calculate base discount for person 
            return 0; 
        } 
}

جمع بندی 

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

مطالب
CoffeeScript #6

Classes

Inheritance & Super

شما می‌توانید به راحتی از کلاس‌های دیگری که نوشته‌اید، با استفاده از کلمه‌ی کلیدی ،extends ارث بری کنید:

class Animal
  constructor: (@name) ->

  alive: ->
    true

class Parrot extends Animal
  constructor: ->
    super("Parrot")

  dead: ->
    not @alive()
در مثال بالا، Parrot (طوطی) از کلاس Animal ارث بری شده، که تمام خصوصیات آن را مانند ()alive، ارث برده است.
همانطوری که در مثال بالا مشاهده می‌کنید، در کلاس Parrot در تابع constructor، تابع super فراخوانی شده است. با استفاده از کلمه‌ی کلیدی super می‌توان تابع سازنده‌ی کلاس پدر را فراخوانی کرد. نتیجه‌ی کامپایل super در مثال بالا به این صورت می‌شود:
Parrot.__super__.constructor.call(this, "Parrot");
تابع super در CoffeeScript دقیقا مانند Ruby و Python عمل می‌کند.
در صورتیکه تابع constructor را در کلاس فرزند ننوشته باشید، به طور پیش فرض CoffeeScript سازنده کلاس پدر را فراخوانی می‌کند.

CoffeeScript با استفاده از prototypal inheritance، به صورت خودکار تمامی خصوصیات کلاس پدر، به فرزندان انتقال پیدا می‌کند. این ویژگی سبب داشتن کلاس‌های پویا می‌شود. برای درک بهتر این موضوع، فرض کنید که خصوصیتی را به کلاس پدر بعد از ارث بری کلاس فرزند اضافه می‌کنید. خصوصیت اضافه شده به تمامی فرزندان کلاس پدر به صورت خودکار اضافه می‌شود.
class Animal
  constructor: (@name) ->

class Parrot extends Animal

Animal::rip = true

parrot = new Parrot("Macaw")
alert("This parrot is no more") if parrot.rip

Mixins

Mixins توسط CoffeeScript پشتیبانی نمی‌شود و برای همین نیاز است که این قابلیت را برای خودمان پیاده سازی کنیم، به مثال زیر توجه کنید.
extend = (obj, mixin) ->
  obj[name] = method for name, method of mixin        
  obj

include = (klass, mixin) ->
  extend klass.prototype, mixin

# Usage
include Parrot,
  isDeceased: true

alert (new Parrot).isDeceased
نتیجه کامپایل آن می‌شود:
var extend, include;
extend = function(obj, mixin) {
  var method, name;
  for (name in mixin) {
    method = mixin[name];
    obj[name] = method;
  }
  return obj;
};
include = function(klass, mixin) {
  return extend(klass.prototype, mixin);
};
include(Parrot, {
  isDeceased: true
});
alert((new Parrot).isDeceased);
Mixins یک الگوی عالی برای به اشتراک گذاشتن خصوصیت‌های مشترک، در بین کلاس‌هایی است که امکان ارث بری در آنها وجود ندارد. مهمترین مزیت استفاده از Mixins این است که می‌توان چندین خصوصیت را به یک کلاس اضافه کرد؛ در حالیکه برای ارث بری فقط از یک کلاس می‌توان ارث بری داشت.

Extending classes


Mixins خیلی مرتب و خوب است اما خیلی شیء گرا نیست؛ در عوض امکان ادغام را در کلاس‌های CoffeeScript ایجاد می‌کند. برای اینکه اصول شیء گرایی را بخواهیم رعایت کنیم و ویژگی ادغام را نیز داشته باشیم، کلاسی با نام Module را پیاده سازی می‌کنیم و تمامی کلاس‌هایی را که می‌خواهیم ویژگی ادغام را داشته باشند، از آن ارث بری می‌کنیم.
moduleKeywords = ['extended', 'included']

class Module
  @extend: (obj) ->
    for key, value of obj when key not in moduleKeywords
      @[key] = value

    obj.extended?.apply(@)
    this

  @include: (obj) ->
    for key, value of obj when key not in moduleKeywords
      # Assign properties to the prototype
      @::[key] = value

    obj.included?.apply(@)
    this
برای استفاده از کلاس Module به مثال زیر توجه کنید:
classProperties = 
  find: (id) ->
  create: (attrs) ->

instanceProperties =
  save: -> 

class User extends Module
  @extend classProperties
  @include instanceProperties

# Usage:
user = User.find(1)

user = new User
user.save()
همانطور که مشاهده می‌کنید دو خصوصیت ثابت (static property)، را به کلاس User اضافه کردیم (find, create) و خصوصیت save.
همچنین برای خلاصه نویسی بیشتر می‌توان از این الگو استفاده کرد (ساده و زیبا).
ORM = 
  find: (id) ->
  create: (attrs) ->
  extended: ->
    @include
      save: -> 

class User extends Module
  @extend ORM
مطالب
الگوی طراحی Factory Method به همراه مثال

الگوی طراحی Factory Method به همراه مثال

عناوین :

·   تعریف Factory Method
·   دیاگرام UML
·   شرکت کنندگان در UML
·   مثالی از Factory Pattern در #C 


تعریف الگوی Factory Method :

این الگو پیچیدگی ایجاد اشیاء برای استفاده کننده را پنهان می‌کند. ما با این الگو میتوانیم بدون اینکه کلاس دقیق یک شیئ را مشخص کنیم آن را ایجاد و از آن استفاده کنیم. کلاینت ( استفاده کننده ) معمولا شیئ واقعی را ایجاد نمی‌کند بلکه با یک واسط و یا کلاس انتزاعی (Abstract) در ارتباط است و کل مسئولیت ایجاد کلاس واقعی را به Factory Method می‌سپارد. کلاس Factory Method می‌تواند استاتیک باشد . کلاینت معمولا اطلاعاتی را به متدی استاتیک از این کلاس می‌فرستد و این متد بر اساس آن اطلاعات تصمیم می‌گیرید که کدام یک از پیاده سازی‌ها را برای کلاینت برگرداند.

از مزایای این الگو این است که اگر در نحوه ایجاد اشیاء تغییری رخ دهد هیچ نیازی به تغییر در کد کلاینت‌ها نخواهد بود. در این الگو اصل DIP از اصول پنجگانه SOLID به خوبی رعایت می‌شود چون که مسئولیت ایجاد زیرکلاس‌ها از دوش کلاینت برداشته می‌شود.


دیاگرام UML :

در شکل زیر دیاگرام UML الگوی Factory Method را مشاهده می‌کنید.

        

شرکت کنندگان در این الگو به شرح زیل هستند :

- Iproduct یک واسط است که هر کلاینت  از آن استفاده می‌کند. در اینجا کلاینت استفاده کننده نهایی است مثلا می‌تواند متد main یا هر متدی در کلاسی خارج از این الگو باشد. ما می‌توانیم پیاده سازی‌های مختلفی بر حسب نیاز از واسط Iproduct ایجاد کنیم.

- ConcreteProduct یک پیاده سازی از واسط Iproduct است ، برای این کار بایستی کلاس پیاده سازی (ConcreteProduct) از این واسط (IProduct) مشتق شود.

- Icreator واسطیست که Factory Method را تعریف می‌کند. پیاده ساز این واسط بر اساس اطلاعاتی دریافتی کلاس صحیح را ایجاد می‌کند. این اطلاعات از طریق پارامتر برایش ارسال می‌شوند.همانطور که گفتیم این عملیات بر عهده پیاده ساز این واسط است و ما در این نمودار این وظیفه را فقط بر عهده ConcreteCreator گذاشته ایم که از واسط Icreator مشتق شده است.


پیاده سازی UMLفوق به صورت زیر است:

در ابتدا کلاس واسط IProduct تعریف شده است.

interface IProduct
{
       //  در اینجا  برحسب نیاز فیلدها  و یا امضای متد‌ها قرار می‌گیرند 
}

در این مرحله ما پند پیاده سازی از IProduct انجام می‌دهیم.

class ConcreteProductA : IProduct
{
      // A پیاده سازی 
}
 
class ConcreteProductB : IProduct
{
      // B پیاده سازی 
}
در این مرحله کلاس انتزاعی Creator تعریف می‌شود.
abstract class Creator
{
          // این متد بر اساس نوع ورودی انتخاب مناسب را انجام و باز می‌گرداند
           public abstract IProduct FactoryMethod(string type);
}
در این مرحله ما با ارث بری از Creator متد Abstract آن را به شیوه خودمان پیاده سازی می‌کنیم.
class ConcreteCreator : Creator
{
     public override IProduct FactoryMethod(string type)
    {
            switch (type)
           {
                case "A": return new ConcreteProductA();
                case "B": return new ConcreteProductB();
                default: throw new ArgumentException("Invalid type", "type");
           }
     }
}
مثالی از Factory Pattern در #C :

برای روشن‌تر شدن موضوع ، یک مثال کاملتر ارائه داده می‌شود. در شکل زیر طراحی این برنامه نشان داده شده است.

کد برنامه به شرح زیل است :

using System;

namespace FactoryMethodPatternRealWordConsolApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            VehicleFactory factory = new ConcreteVehicleFactory();

            IFactory scooter = factory.GetVehicle("Scooter");
            scooter.Drive(10);

            IFactory bike = factory.GetVehicle("Bike");
            bike.Drive(20);

            Console.ReadKey();

        }
    }

    public interface IFactory
    {
        void Drive(int miles);
    }

    public class Scooter : IFactory
    {
        public void Drive(int miles)
        {
            Console.WriteLine("Drive the Scooter : " + miles.ToString() + "km");
        }
    }

    public class Bike : IFactory
    {
        public void Drive(int miles)
        {
            Console.WriteLine("Drive the Bike : " + miles.ToString() + "km");
        }
    }

    public abstract class VehicleFactory
    {
        public abstract IFactory GetVehicle(string Vehicle);

    }

    public class ConcreteVehicleFactory : VehicleFactory
    {
        public override IFactory GetVehicle(string Vehicle)
        {
            switch (Vehicle)
            {
                case "Scooter":
                    return new Scooter();
                case "Bike":
                    return new Bike();
                default:
                    throw new ApplicationException(string.Format("Vehicle '{0}' cannot be created", Vehicle));
            }
        }
    }
}
خروجی اجرای برنامه فوق به شکل زیر است :






فایل این برنامه ضمیمه شده است، از لینک مقابل دانلود کنید FactoryMethodPatternRealWordConsolApp.zip

در مقالات بعدی مثال‌های کاربردی‌تر و جامع‌تری از این الگو و الگو‌های مرتبط ارائه خواهم کرد... 
مطالب
تفاوت‌های پروژه‌های ما و پروژه‌های اونا!

چندی قبل پروژه‌ای دولتی در زمینه‌ی غلط یابی متون فارسی منتشر شد؛ اما ... آنچنان بازتابی در بین سایت‌های ایرانی پیدا نکرد. حداکثر بازتاب آن مقاله‌ی روزنامه همشهری در این حد بود که "مایکروسافت که یکی برامون درست کرده بود! تو چرا بی خود زحمت کشیدی!". البته روزنامه‌ی همشهری هم مقصر نیست؛ چون به طور قطع نویسنده‌ی آن با توانایی‌های این برنامه در مقایسه با غلط یاب ساده مایکروسافت اطلاعات آنچنانی ندارد.
از سایت‌های ایرانی هم نباید انتظار داشت که برای محصولات ایرانی تبلیغ کنند! اگر به سایت‌های ایرانی فعال در زمینه‌ی IT دقت کنید بیشتر ذوق کردن‌های آن‌ها ناشی از کارهای شرکت خارجی است؛ مثلا:
- گوگل امروز در صفحه‌ی اولش یک عکس جدید گذاشته! (2000 تا سایت این رو پوشش می‌دن!)
- رئیس جدید مایکروسافت دیروز که می‌خواست بره سخنرانی دوبار جیغ کشید، سه بار دور سالن دوید گفت اینجا عجب شرکت باحالیه!
- استیوجابز خیلی مرد نازنینی است چون فقط یک دست شلوار لی و پولیور مشکی دارد که 10 سالش است همین فقط تنشه (هم... از شما چه پنهون وضع خود من هم بهتر از این نیست:) )
- فیس بوک امروز عکس‌های شما رو در پروفایلتون هم نمایش می‌ده!
وب سایت‌های لینوکسی ایرانی هم که یک چیزی توی این مایه‌ها هستند:
این مایکروسافت سه تا نقطه (!) باز یک سیستم عامل جدید بیرون داد، ولی اوبونتوی ما 5 ثانیه زودتر میاد بالا؛ به همین جهت هفته بعد قراره پس از طی مراحل نصب سیستم عامل که از امروز شروع میشه به همراه کلیه دوستانی که در این امر مهم دخیل هستند جشن بگیریم! زنده باد آزادی!

و ... سؤال اصلی اینجا است: چرا مدل توسعه‌‌ای که از آن صحبت شد نمی‌فروشه؟! چرا کسی برای آن تبلیغ نکرد؟ چرا آنچنان کسی ذوق زده نشد؟! چرا زود فراموش شد؟

خوب؛ حالا بد نیست نگاهی هم داشته باشیم به مدل توسعه‌ی "اونا"

یکی از این "اونا" مثلا می‌تونه تیم سیلورلایت مایکروسافت باشه. بیائیم ببینیم "اونا" چکار می‌کنند تا محصولاتشون بفروشه؛ ملت از شنیدن اخبار اون‌ها ذوق زده بشن؛ هزار‌تا سایت کارشون رو به رایگان تبلیغ کنند و ...
- "اونا" یا کلا هر تیم توسعه‌ای تشکیل شده از یک سری آدم! هر کدام از این‌ها یا در سایت MSDN یا به طور جداگانه وبلاگ دارند و کاری رو که به طور منظم انجام می‌دن، ارسال جزئیاتی اندک از پیشرفت‌های حاصل شده است. مثلا آقای TimHeuer هر از چندگاهی این کار رو می‌کنه. به این صورت دیگه روزنامه همشهری چاپ نیویورک نمیاد بنویسه، Adobe که یکی برامون درست کرده بود! تو چرا بی‌خود زحمت کشیدی! چون الان روزنامه‌ی نیویورک تایمز می‌دونه جزئیات کاری که انجام شده به مرور زمان چی بوده.
- "اونا" قسمتی رو در سایت MSDN دارند به نام Silverlight TV . یک نفر رو هم استخدام کردن به نام آقای جان پاپا! تا بیاد براشون با اعضای مختلف تیم مصاحبه کنه و یک سری از جزئیات رو بیشتر برای عموم مردم توضیح بده.
- "اونا" هر از چندگاهی سمینار برگزار می‌کنند: (+). میان پز می‌دن ما اینکار رو کردیم اونکار رو کردیم؛ دنیا، بدونید ما چقدر عالی هستیم!
- "اونا" یک bug tracking system دارند. یک features suggestion system دارند: (+) . مردم الان می‌دونند اگر باگی رو در سیستم پیدا کردند، کجا باید گزارش بدن. اگر نیاز به ویژگی جدیدی داشتند باید چکار کنند. صرفا با یک صفحه‌ی ثابت که لینک دریافت دو تا فایل رو گذاشته مواجه نیستند.
- "اونا" یک فوروم مخصوص هم برای Silverlight درست کردند تا استفاده کننده‌ها بیان ابتدایی‌ترین تا پیشرفته‌ترین سوالات خودشون رو مطرح کنند. نرفتند اون پشت قایم بشن! یا بگن این ایمیل ما است؛ دوست داشتید ایمیل بزنید ما هم وقت کردیم جواب می‌دیم!
- "اونا" مستندات و راهنمای بسیار کامل، قوی و قابل مرور تحت وب دارند: (+).
- "اونا" اگر کارشون را رایگان و سورس باز می‌خواهند ارائه دهند از یک سورس کنترل استفاده می‌کنند: (+).به رها کردن یک تکه سورس کد در ملاء عام کار سورس باز نمی‌گن! هر کاری آداب و اصول خودش رو داره। شما که نمیای بچه‌ت رو بذاری سر راه و بری؟! به این امید که خودش patch میشه، خودش به روز میشه، یکی پیدا میشه تا دستی به سرش بکشه!
البته این مورد جدیدا جهت پروژه غلط یاب یاد شده راه اندازی شده: (+) ولی فکر نمی‌کنم کسی متوجه شده باشه، چرا؟! چون اخبار رو این روزها علاقمندان از طریق فیدهای RSS دنبال می‌کنند؛ نه با مراجعه‌ی هر روزه به یک سایت. انتظار بی‌موردی است که استفاده‌ کنندگان هر روز به سایت ما سر بزنند تا ببینند چه خبره! به همین جهت RSS اختراع شده. همچنین پروژه اختصاصی فارسی و سورس کنترل انگلیسی هم همخوانی ندارند.
- "اونا" اکانت توئیتر دارند: (+). "اونا" اکانت فیس بوک دارند: (+). "اونا" از این امکانات برای گزارش دادن رخدادهای داخلی خودشون استفاده می‌کنند. یکی از ابزارهای مهم تبلیغاتی اون‌ها است. کلا رسانه‌ها رو در دنیای غرب به عصر قبل و بعد از توئیتر تقسیم بندی می‌کنند. پیش از توئیتر اخبار تهیه می‌شد و تبدیل به خوراک اطلاعاتی عموم می‌شد؛ الان اطلاعات موجود در توئیتر، جمع آوری، آنالیز و تحلیل می‌شود و سپس تبدیل به خوراک خبرگزاری‌ها می‌گردد.
- "اونا" برای ارائه دانلود‌هاشون دیتاسنتر اختصاصی دارند. جایی خوندم مساحت دیتاسنتر‌های فعلی گوگل در حد یکی از ایالت‌های آمریکا شده ...؛ تصورش رو بکنید که با یک وب سایت هاست شده در یک کشور ثالث، بخواهید یک نرم افزار 200 مگی را به هزاران نفر عرضه کنید. یا مشکل پنهای باند پیدا می‌کنید یا کند شدن سرور یا ...
- "اونا" اگر وقت کنند هر از چندگاهی برای تبلیغ و توسعه کارشون کتاب هم منتشر می‌کنند: (+)، تعدادی از این‌ها هم رایگان است.

و ... و ... بدون رعایت این موارد پروژه‌های خوب ارائه شده در اینترنت نمی‌فروشند! حتی شما دوست عزیز!

مطالب
5 دلیل برای استفاده از یک ابزار ORM

چرا باید از ابزارهای Object relational Mapper یا به اختصار ORM استفاده کرد؟ در اینجا سخن در مورد ORM خاصی نیست. هدف تبلیغ یک محصول ویژه هم نمی‌باشد و یک بحث کلی مد نظر است.
کار ابزارهای ORM خواندن ساختار دیتابیس شما بوده و سپس ایجاد کلاس‌هایی بر اساس این ساختار ، برقراری ارتباط بین اشیاء ایجاد شده و جداول، ویووها، رویه‌های ذخیره شده و غیره می‌باشد. همچنین این ابزارها امکان تعریف روابط one-to-one, one-to-many, many-to-one, و many-to-many بین اشیاء را نیز بر اساس ساختار دیتابیس شما فراهم می‌کنند.
در ادامه به فواید استفاده از ORM ها خواهیم پرداخت:

الف) یک ابزار ORM زمان تحویل پروژه را کاهش می‌دهد

اولین و مهم‌ترین دلیلی که بر اساس آن در یک پروژه، استفاده از ORM حائز اهمیت می‌شود، بحث بالا بردن سرعت برنامه نویسی و کاهش زمان تحویل پروژه به مشتری است. این کاهش زمان بسته به نوع پروژه بین 20 تا 50 درصد می‌تواند خود را بروز دهد.
بدیهی است ابزارهای ORM کار شگفت انگیزی را قرار نیست انجام دهند و شما می‌توانید تمام آن عملیات ‌را دستی هم به پایان رسانید؛ اما اجازه دهید یک مثال کوتاه را با هم مرور کنیم.
برای پیاده سازی یک برنامه متداول با حدود 15 تا 20 جدول، حدودا به 30 شیء برای مدل سازی سیستم نیاز خواهد بود و برنامه نویسی این مجموعه بین 5000 تا 10000 سطر کد را به خود اختصاص خواهد داد. بدیهی است برنامه نویسی و آزمایش این سیستم چندین هفته یا ماه به طول خواهد انجامید.
اما با استفاده از یک ORM ، عمده وقت شما به طراحی سیستم و ایجاد ارتباطات بین اشیاء و دیتابیس در طی یک تا دو روز صرف خواهد شد. ایجاد کد بر اساس این مجموعه و با کمک ابزارهای ORM ، آنی است و با چند کلیک صورت می‌گیرد.


ب) یک ابزار ORM کدی با طراحی بهتر را تولید می‌کند

ممکن است شما بگوئید که کد نویسی من بی‌نظیر است و از من بهتر کسی را نمی‌توانید پیدا کنید! به تمامی زوایای کار خود مسلطم و نیازی هم به این‌گونه ابزارها ندارم!
عده‌ای از شما به طور قطع این‌گونه‌اید؛ اما نه همه. در یک تیم متوسط، همه نوع برنامه نویس با سطوح مختلفی را می‌توانید پیدا کنید و تمامی ‌‌آن‌ها برنامه نویس‌ها و یا طراح‌های آنچنان قابلی هم نیستند. بنابراین امکان رسیدن به کدهایی که مطابق اصول دقیق برنامه نویسی شیء گرا نیستند و در آن‌ها الگوهای طراحی به خوبی رعایت نشده، بسیار محتمل است. همچنین در یک تیم زمانیکه از یک الگوی یکسان پیروی نمی‌شود، نتایج نهایی بسیار ناهماهنگ خواهند بود.
در مقابل استفاده از ORM های طراحی شده توسط برنامه نویس‌های قابل (senior (architect level) engineers) ، کدهایی را بر اساس الگوهای استاندارد و پذیرفته شده‌ی شیء‌گرا تولید می‌کنند و همواره یک روند کاری مشخص و هماهنگ را در یک مجموعه به ارمغان خواهند آورد.

ج) نیازی نیست تا حتما یک متخصص دات نت فریم ورک باشید تا از یک ORM استفاده کنید

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


د) هنگام استفاده از یک ابزار ORM ، مدت زمان آزمایش برنامه نیز کاهش می‌یابد

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

ه) استفاده از یک ابزار ORM ، کار برنامه نویسی شما را ساده‌تر می‌کند

توضیح این قسمت نیاز به ذکر یک مثال دارد. لطفا به مثال زیر دقت بفرمائید:

try {
Employees objInfo = new Employees();
EmployeesFactory objFactory = new EmployeesFactory();

objInfo.EmployeeID = EmployeeID;
objFactory.Load(objInfo);

// code here to use the "objInfo" object
}
catch(Exception ex) {
// code here to handle the exception
}

به نظر شما کار کردن با یک یا چند شیء تولید شده که نمایانگر ساختار دیتابیس شما هستند و با استفاده از اینترفیس عمومی آن‌ها می‌توان تمامی اعمال بارگذاری، درج و حذف و غیره را انجام داد، ساده‌تر است یا کار کردن با کوهی از دستورات ADO.Net ؟


برداشتی آزاد از Five Reasons for using an ORM Tool

مطالب
شروع کار با Apache Cordova در ویژوال استودیو #3
در قسمت قبل توانستیم ابزارهای لازم را برای Apache Cordova، نصب کنیم. در این قسمت یک پروژه‌ی ساده را ایجاد کرده و در مورد ساختار آن توضیح خواهم داد. در ادامه‌ی مقالات از  AngularJS ، Bootstrap ,Typescript و jQuery Mobile  هم در پروژه‌ها استفاده خوهیم کرد.
برای شروع، از قسمت JavaScript یا Typescript، یک پروژه‌ی از نوع Blank App ایجاد کنید. به شکل زیر:


ترجیحا نوع Typescript را انتخاب کردم. البته در داخل فایل ts. امکان نوشتن جاوا اسکریپت هم هست. بعد از ایجاد پروژه اگر با تصویری شبیه به تصویر زیر روبرو شدید، در نتیجه تنظیمات نصب و راه اندازی به درستی صورت گرفته است.



اگر به قسمت solution explorer دقت کنید، فایلی به نام config.xml را مشاهده خواهید کرد. با کلیک بر روی این فایل، یک صفحه‌ی گرافیکی باز خواهد شد که این امکان را به شما می‌دهد که پلاگین‌های مورد نیاز خود، تنظیمات مربوط به نرم افزار تولیدی (مانند تنظیم ورژن ویندوزی که می‌خواهید app شما بر روی آن اجرا شود) و تنظیمات مربوط به هر یک از پلتفرم‌ها را به صورت مجزا در اختیار داشته باشید.



یک فایل index.html هم در قالب پیش‌فرض قرار داده شده که بعدا می‌توانید آن را تغییر دهید و یا صفحات دیگری را اضافه کنید. همان طور که در قسمت‌های قبل گفته شد، قرار است ما یک وب اپلیکیشن طراحی کنیم و آن را درون Container بومی Cordova بسته بندی کنیم. لذا محدودیتی برای استفاده‌ی از کتابخانه‌های مرتبط با CSS ، HTML و JavaScript  نداریم و در ادامه‌ی مقالات با مثال‌های متعددی از آن‌ها استفاده خواهیم کرد.

در فولدر scripts-->typeings-->cordova-->plugins  اینترفیس‌هایی که برای دسترسی به امکانات بومی دستگاه تلفن فعلا در Cordova پشتیبانی می‌شوند، قرار گرفته است.

برای استفاده از تکنولوژی‌های وب در محیط بومی دستگاه، در طی فرآیند کامپایل، Cordova یک اپلیکیشن را به وسیله دو چیز مهم که در زیر اشاره شده است، خواهد ساخت.


  • یک اپلیکیشن با یک کامپوننت  WebView که با مرورگر یکپارچه شده است.
  • یه سری از منابعی که در داخل فایل‌های اپلیکیشن وب ما قرار دارند.


برای یکپارچه شدن API‌های Cordova با وب پیج موجود، اندکی کد نیاز داریم که برای انکار لینکی شبیه لینک زیر را در فایل html خود استفاده می‌کنیم که فقط بعد از کامپایل وجود خارجی دارد؛ به صورت زیر:

<script src="cordova.js"></script>

در پایان هم برای فهمیدن اینکه API‌های Cordova در دسترس هستند، می‌توانیم رخداد مربوط به devicerady را مدیریت کنیم؛ به صورت زیر:

document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() { /* INIT */ }

برای مدیریت رخدادهای مربوط به pause و resume هم که نشان دهنده‌ی ادامه برنامه (خارج شدن از حالت pause) و حالت تعلیق هستند، می‌توان به شکل زیر عمل کرد:

 function onDeviceReady() {
            // Handle the Cordova pause and resume events
            document.addEventListener('pause', onPause, false);
            document.addEventListener('resume', onResume, false);

            // TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
        }

        function onPause() {
            // TODO: This application has been suspended. Save application state here.
        }

        function onResume() {
            // TODO: This application has been reactivated. Restore application state here.
        } 

حال قصد داریم پروژه‌ی خود را که قرار است یک متن ساده را نشان دهد، با استفاده از شبیه ساز اجر ا کنیم. برای این منظور از قسمت toolbar ویژوال استودیو ، Solution Platform خود را انتخاب کنید و سپس می‌توانید شبیه ساز مورد نظر خود را انتخاب کرده و برنامه را اجرا کنید. در اینجا محیط مورد نظر من اندروید است  و برای این منظور هم میتوانم از شبیه ساز Android Emulator یا Ripple استفاده کنم.  به دلیل سرعت کم شبیه ساز اندروید، می‌توانید شبیه ساز  YouWave را دانلود و اجرا کرده و در قسمتی که شبیه ساز را از toolbar ویژوال انتخاب می‌کردید، این بار گزینه‌ی Device را انتخاب کنید. بعد از کامپایل برنامه‌ی شما، فایل apk تولید شده بر روی شبیه ساز نصب خواهد شد و شما قادر خواهید بود آنرا اجرا کنید.

نتیجه‌ی نهایی 

 با شبیه ساز Ripple 


مطالعه بیشتر

https://msdn.microsoft.com/en-us/library/dn879821(v=vs.140).aspx 

http://blog.falafel.com/getting-started-with-cordova-and-multi-device-hybrid-app-in-visual-studio/ 

http://www.codeproject.com/Articles/860150/Visual-Studio-and-Apache-Cordova 


نکته : وقتی پروژه را برای اولین بار اجرا می‌کنید شاید کمی طول بکشد تا نتیجه‌ی نهایی را ببنید و آن هم به دلیل این است که ویژوال استودیو  باید مجموعه‌ای از package  های مورد نیاز Cordova را دانلود کند.

در مقاله بعد با jQuery Mobile آشنا خواهیم شد و یک مثال برای کار کردن با آن در نظر خواهم گرفت.


ادامه دارد ...

نظرات مطالب
ساختار پروژه های Angular
ضمن تشکر فراوان از جناب آقای پاکدل عزیز، در این مقاله به خوبی درباره lazy loading در angularjs بحث شده. نکته مهم اینکه حتما پروژه‌ی قابل اجرایی که در انتهای مقاله لینک شده را ملاحظه کنید. نکاتی در این پروژه هست از جمله اینکه برای دسترسی به providerها برای lazy loading آنها به این ترتیب به app افزوده شده اند:
app.config([
        '$stateProvider',
        '$urlRouterProvider',
        '$locationProvider',
        '$controllerProvider',
        '$compileProvider',
        '$filterProvider',
        '$provide',


        function ($stateProvider, $urlRouterProvider, $locationProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
            //برای رجیستر کردن غیر همروند اجزای انگیولاری در آینده
            app.lazy =
            {
                controller: $controllerProvider.register,
                directive:  $compileProvider.directive,
                filter:     $filterProvider.register,
                factory:    $provide.factory,
                service:    $provide.service
            };
.
.
.
])
(البته این کد از پروژه خودمان است و بعضی وابستگی‌های دیگر هم تزریق شده‌اند).
استفاده از app.lazy باعث سهولت بیشتر در استفاده و خواناتر شدن کد می‌شود. در ادامه به این ترتیب می‌توانید از app.lazy استفاده کنید:
angular.module('app').lazy.controller('myController',
        ['$scope',  function($scope){
...
}]);
به این ترتیب کد نوشته شده به دلیل نام گذاری ارجاع controllerProvider  با  controller  به حالت عادی شبیه است، و از طرفی lazy پیش از آن به فهم ماجرا کمک خواهد کرد.
این نقطه شروع یکی از پروژه‌های ماست که به عنوان نمونه بد نیست ملاحظه کنید:
<script type="text/javascript">
// --- Scriptjs ---
!function (a, b, c) { function t(a, c) { var e = b.createElement("script"), f = j; e.onload = e.onerror = e[o] = function () { e[m] && !/^c|loade/.test(e[m]) || f || (e.onload = e[o] = null, f = 1, c()) }, e.async = 1, e.src = a, d.insertBefore(e, d.firstChild) } function q(a, b) { p(a, function (a) { return !b(a) }) } var d = b.getElementsByTagName("head")[0], e = {}, f = {}, g = {}, h = {}, i = "string", j = !1, k = "push", l = "DOMContentLoaded", m = "readyState", n = "addEventListener", o = "onreadystatechange", p = function (a, b) { for (var c = 0, d = a.length; c < d; ++c) if (!b(a[c])) return j; return 1 }; !b[m] && b[n] && (b[n](l, function r() { b.removeEventListener(l, r, j), b[m] = "complete" }, j), b[m] = "loading"); var s = function (a, b, d) { function o() { if (!--m) { e[l] = 1, j && j(); for (var a in g) p(a.split("|"), n) && !q(g[a], n) && (g[a] = []) } } function n(a) { return a.call ? a() : e[a] } a = a[k] ? a : [a]; var i = b && b.call, j = i ? b : d, l = i ? a.join("") : b, m = a.length; c(function () { q(a, function (a) { h[a] ? (l && (f[l] = 1), o()) : (h[a] = 1, l && (f[l] = 1), t(s.path ? s.path + a + ".js" : a, o)) }) }, 0); return s }; s.get = t, s.ready = function (a, b, c) { a = a[k] ? a : [a]; var d = []; !q(a, function (a) { e[a] || d[k](a) }) && p(a, function (a) { return e[a] }) ? b() : !function (a) { g[a] = g[a] || [], g[a][k](b), c && c(d) }(a.join("|")); return s }; var u = a.$script; s.noConflict = function () { a.$script = u; return this }, typeof module != "undefined" && module.exports ? module.exports = s : a.$script = s }(this, document, setTimeout)

$script(['/Scripts/Lib/jquery/jquery-1.10.2.min.js'], function () {
    $script(['/Scripts/Lib/angular/angular.js'], function () {
        $script(['/Scripts/Lib/angular/angular-ui-router.min.js',
                    '/Scripts/Lib/angular/angular-resource.min.js',
                    '/Scripts/Lib/angular/angular-cache.min.js',
                    '/Scripts/Lib/angular/angular-sanitize.min.js',
                    '/Scripts/Lib/angular/angular-animate.min.js',
                    '/Scripts/Lib/angular/angular-cookie.min.js',
                    '/APP/Common/directives.js'
                ], function () {
                    $script('/app/app.js', function () {
                        angular.bootstrap(document, ['app']);
                    });
                });
            })
        });
</script>

این تگ script در صفحه شروع پروژه آمده است.
کد minify شده scriptjs در ابتدا قرار دارد، پس از آن فایل‌های js مورد نیاز با رعایت وابستگی‌های احتمالی به ترتیب بارگذاری شده‌اند.
این قسمت resolve یکی از بخش‌های مسیریابی است: 
resolve: {
                        fileDeps: ['$q', '$rootScope', function ($q, $rootScope) {
                            var deferred = $q.defer();
                            var deps = ['/app/HotStories/dataContextService.js',
                                        '/app/HotStories/hotStController.js'];
                            $script(deps, function () {
                                $rootScope.$apply(function () {
                                    deferred.resolve();
                                });
                            });

                            return deferred.promise;
                        }]
                    }
این نحوه تعریف سرویسی که فایل آن در وابستگی‌ها آمده و قرار است lazy load شود:
angular.module('app').lazy.service('dataContextService',
        ['$rootScope', '$resource', '$angularCacheFactory', '$q', function($rootScope, $resource, $cacheFactory, $q){
...
}]);
و این هم نحوه تعریف کنترلری که فایل آن در وابستگی‌ها آمده و قرار است lazy load شود: 
angular.module('app').lazy.controller('hotStController',
        ['$scope', 'ipCookie', 'dataContextService', function($scope, ipCookie, dataContextService){
...
}]);


دوره‌ها
پشتیبانی از Full Text Search در SQL Server
در این دوره به نحوه‌ی جستجوی پیشرفته در متون، توسط SQL Server خواهیم پرداخت. با استفاده از امکانات ابتدایی T-SQL مانند like می‌توان جستجوهایی را برای یافتن موارد مشابه با عبارتی خاص انجام داد، اما این جستجوها بسیار هزینه‌بر و کند هستند. در SQL Server برای مدیریت جستجوهای سریع و پیشرفته بر روی متون، افزونه‌های توکاری مانند Full text search، Semantic search، Term extraction و Term lookup تدارک دیده شده‌اند. Semantic search از نگارش 2012 آن افزوده شده‌است و مابقی در نگارش‌های پیشین آن نیز وجود داشته‌اند.
نظرات اشتراک‌ها
پیش نمایش Rider 2019.1
من خیلی وقت هست ویژوال استودیو را از سیستم حذف کردم. نه به روز رسانی‌های آن‌را دنبال می‌کنم و نه دیگر هیچ نگارشی از آن را نصب خواهم کرد. برای کار با NET Core. که تعداد به روز رسانی‌های آن بالا است، فقط به یک SDK کم حجم نیاز هست و یک ادیتور. برای این منظور VSCode خوب هست؛ اما این ادیتور (نه IDE) فعلا از افزونه‌هایی مانند ReSharper و سایر افزونه‌های مبتنی بر Roslyn پشتیبانی نمی‌کند. Rider یک IDE چندسکویی کامل یکپارچه با ReSharper هست (یعنی نیازی نیست ReSharper را جداگانه دریافت و بر روی آن نصب کنید؛ هر دو متعلق به یک شرکت هستند) و برای کار با NET Core. می‌تواند انتخاب اول باشد؛ آن هم فقط با 500 مگابایت حجم.
نظرات مطالب
معرفی DNTBreadCrumb
شما بجای استفاده از url friendly یا هر افزونه‌ی ثالثی، فقط باید مسیریابی متناظری را تعریف کنید، تا این آدرس‌ها به صورت استاندارد و قابل استفاده‌ی در تمام قسمت‌های برنامه تولید شوند. MVC نیازی به افزونه‌های url friendly ندارد؛ چیزی شبیه به این و یا مثال «سازگار کردن لینک‌های قدیمی یک سایت با ساختار جدید آن در ASP.NET MVC»:
routes.MapRoute(
    "category_route", // Route name
    "MyControllerName/{category}/{productname}", // URL with parameters
    new { controller = "MyControllerName", action = "MyActionName",
             category = UrlParameter.Optional, productname = UrlParameter.Optional, area = "" }
);