Pull to refresh

Ликвидатор велосипедов: повторно используемый код

.NET *
Всем привет! Помните, когда-то давным давно была хорошая серия статей? Так вот я хочу ее продолжить. Однако на этот раз я не буду рассматривать огромное множество библиотек, которые решают повсеместно-возникающие проблемы, а рассмотрю всего лишь одну, но способную решить огромную кипу проблем. Мне кажется каждый из вас когда-то писал строчки вида:

void SomeMethod(IEmployee lazyguy)
{
    if(lazyguy == null) throw new ArgumentException(“lazyguy”);
    // Do something with lazy guy. Fire him, for example.
}


Кто-то пытался превратить это в:

void SomeMethod(IEmployee lazyguy)
{
    UniversalValidator.CheckNotNull(lazyguy);
    // Meet with him and talk about motivations
}


Кто-то превращал это во что-то похожее. Однако у всех, я могу поспорить, оставался осадок что что-то тут не так. Красивее было бы как-то по другому. А что в итоге? Огромное количество велосипедов с квадратными колесами! Как сделать по-другому?

Так вот по-другому, когда душа радуется и код чистый, это когда пишешь так:
void SomeMethod([NotNull] IEmployee lazyguy)
{
    // Promote him to be head of department
}

Согласитесь, приятно! Вы можете мне возразить, сказав что атрибуты – это все равно что теги к статье: пока их никто не прочитает, магия не сработает. И вы будете правы. Та программа, о которой я хочу рассказать – PostSharp, позволяет делать такие вещи. Вы пишите атрибут, который будет прикладываться к параметрам метода и который будет вставлять в код программы проверку на null. Если null, то генерируем exception. С этого момента вам не надо засорять каждый метод мусором. Теперь достаточно лишь пометить необходимые методы необходимыми атрибутами:

public class LazyEmployee : IEmployee
{
    string Name { get; }
    string LastName { get; }
   
    [GreaterThan(VeryCleverGuySalary)]
    int Salary { get; set; }

    [Only(true)]
    bool NeedsPersonalSecretary { get; set; }

    [TypeOf(typeof(IRoomWithSaunaAndPersonalPlayStation))]
    IRoom Room { get; set; }
}


Теперь поговорим о другой проблеме, которая обсуждалась уже много раз, но люди упорно делают свои логгеры, свои трейсеры и прочие радости велосипедостроения (кстати, а вы знаете что недавно сделали велосипед с “автоматом”?). Хорошо что сделали такие вещи как log4net, например. Однако это не всегда работает так быстро как хочется. С нашей программой внедрение логгирования будет работать максимально быстро, а само логгирование будет работать со скоростью самописного кода:
public class PerisherEmployee : IEmployee
{
    [NotNull, StartingWithUpperCase]
    string Name { get; }

    [NotNull, StartingWithUpperCase]
    string Lastname { get; }

    [Tracing, WriteToDatabaseEachTransaction, CheckHistory]
    int Salary{ get; set; }

    [AbuseEachWhoTriesToBuySomething, Trace]
    int FreeMoney { get; set; }
}


В этом примере некий скряга хочет чтобы каждое действие с его денежными средствами записывалось бы в базу данных, при этом проверялась бы история операций и обругивались все кто пытается на эти деньги что-то купить. Пример, конечно, выдуманный и эти атрибуты (кроме трассировки) вам вряд ли понадобятся, однако они ясно показывают возможности происходящего. Тут вам и работа с базой и трассировка и проверка значений на диапазоны. Вы можете возразить: вот буду я городить непонятное облако атрибутов, загромождать пространство! Однако, попробую спарировать: разве проще выглядит такой код?

public class PerisherEmployee : IEmployee
{
    string _name, _lastname;
    int  _salary, _freemoney;

    string Name 
    { 
        set {
            if(value == null) throw new ArgumentNullException("value"); 
            if(!IsStartsFromUpperCase(value)) throw new ArgumentOutOfRangeException("value");
            _name = value;
        }
        get {
            return _name;
        }
    }

    string Lastname
    { 
        set {
            if(value == null) throw new ArgumentNullException("value"); 
            if(!IsStartsFromUpperCase(value)) throw new ArgumentOutOfRangeException("value");
            _surname = value;
        }
        get {
            return _surname;
        }
    }


    [Tracing, WriteToDatabaseEachTransaction, CheckHistory]
    int Salary
    { 
        get {
            return _salary;
        }      
        set {
            Console.WriteLine("value: {0}", value);
            DatabaseEngine.SaveSalary(value);
           _salary = value;
            if(!CheckHistory(this)) throw new WrongOperationException();
        }
    }

    int FreeMoney 
    { 
        get 
        {
            return _freememory;
        }
        set
        {
            if(value < _freememory) throw new OutOfRangeException();
           Console.WriteLine("value: {0}", value);
        }
    }
}

Совсем нет, но решать вам :) Существует также большое количество вариантов, когда это работает отлично! Когда это работает на 10 баллов из 5. Например, трассировка сообщений на ASP.NET в консоль разработчика в то время как веб-сайт уже работает… Вы можете посмотреть историю стек-трейса на любой момент работы веб-странички. Как? Просто напишите аспект, который будет сохранять стек куда-нибудь и наложите на сборку. Или какие-то еще на первый взгляд, странные вещи. Я расскажу о них чуть позже… А пока просто приведу ряд ссылок на статьи, в которых я уже рассказывал о фичах, реализуемых этой программой:

Tags:
Hubs:
Total votes 21: ↑13 and ↓8 +5
Views 1.8K
Comments Comments 8