Q:

C# Перенос валидации свойств класса в другой класс

Всем доброго времени суток).
Скажите, пожалуйста, допустим ли приведенный ниже подход при валидации полей класса. Суть подхода заключается в вынесении логики валидации полей класса из setter в методы класса-валидатора?
При использовании данного подхода можно будет менять правила валидации заданного класса. Кроме того, класс-валидатор можно будет повторно использовать в других участках программы, например при валидации ввода данных из окна программы (в ViewModel при использовании MVVM).
Пример реализации данного подхода для простого класса, хранящего информацию о книге. Мне приходилось сталкиваться с классами, в которых присутствует порядка 16 параметров, для каждого из которых задавалось свое правило проверки. 
using System;
using System.Collections.Generic;

public class Program {
    public static void Main() {
        IBookValidator bookValidator = new BookValidator();

        IBookInfo bookInfo = new BookInfo() {
            Author = "Толстой",
            Name = "Война и мир",
            Price = -1
        };

        try {
            Book book = new Book(bookInfo, bookValidator);
        }
        catch(ArgumentException ex) {
            Console.WriteLine(ex.Message);
        }
    }
}

// Книга
public class Book : IBook {
    public event EventHandler<IBookInfo> BookParamsUpdated;

    public double Price { get; private set; }
    public string Author { get; private set; }
    public string Name { get; private set; }

    public Book(IBookInfo newBook, IBookValidator bookValidator) {
        // Пример проверки цены книги
        bool isParamsValid = bookValidator.Validate(newBook);

        if (isParamsValid == false) {
            // В упрощенном виде достаем текст ошибки
            throw new ArgumentException(bookValidator.Errors[0].Error);
        }
        else {
            Update(newBook);
        }
    }

    // Обновление параметров книги
    public void Update(IBookInfo bookData) {
        this.Price = bookData.Price;
        this.Author = bookData.Author;
        this.Name = bookData.Name;

        OnBookParamsUpdated();
    }

    private void OnBookParamsUpdated() {
        if (BookParamsUpdated != null) {
            BookParamsUpdated(this, new BookInfo(this));
        }
    }
}

// Параметры книги
public class BookInfo : IBookInfo {
    public double Price { get; set; }
    public string Author { get; set; }
    public string Name { get; set; }

    public BookInfo() {

    }

    public BookInfo(IBookInfo bookInfo) {
        Price = bookInfo.Price;
        Author = bookInfo.Author;
        Name = bookInfo.Name;
    }
}

// Описывает параметры книги
public interface IBookInfo {
    double Price { get; }
    string Author { get; }
    string Name { get; }
}

// Описывает книгу
public interface IBook : IBookInfo {
    void Update(IBookInfo bookInfo);

    event EventHandler<IBookInfo> BookParamsUpdated;
}

public class BookValidator : Validator<IBookInfo>, IBookValidator {
    public BookValidator() : base() {

    }

    // Проверяет параметры книги
    public override bool Validate( IBookInfo bookInfo ) {

        bool validResult = true;

        Errors.Clear();

        string errorPrice = "";
        bool validPrice = ValidatePrice( bookInfo.Price, out errorPrice);
        Errors.Add(new ValidationResult("Price", errorPrice));

        if (Errors.Count != 0) {
            validResult = false;
        }
        return validResult;
    }

    // Проверяет цену книги
    public bool ValidatePrice( double price, out string error) {

        bool isValid = true;
        error = "";
        if (price < 0) {
            error = "Стоимость книги < 0";
            isValid = false;
        }

        return isValid;
    }
}

// Описывает валидатор книги
public interface IBookValidator : IValidator<IBookInfo> {

    // Проверяет корректность стоимости книги
    bool ValidatePrice(double price, out string error);
}

// Результат валидации
public class ValidationResult {
    // Имя проверяемого параметра
    public string ParameterName { get; private set; }

    // Текст ошибки валидации
    public string Error { get; private set; }

    public ValidationResult(string parameterName, string errorMessage) {
        ParameterName = parameterName;
        Error = errorMessage;
    }
}

// Абстрактный Валидатор
public abstract class Validator<T> : IValidator<T> where T : class {
    public Validator() {
        Errors = new List<ValidationResult>();
    }

    // Список ошибок
    public List<ValidationResult> Errors { get; private set; }

    // Валидация объекта
    public abstract bool Validate(T objectForValidation);
}

// Описывает валидацию
public interface IValidator<T> where T : class {

    // Список ошибок валидации
    List<ValidationResult> Errors { get; }

    // Валидация
    bool Validate(T objectForValidation);
}

A:

Данный подход может вполне считаться удачным и иметь положительный эффект на структуру кода. Подобные практики даже описываются в некоторой литературе. 
Например, Марк Симман, в книге "Внедрение зависимостей в .NET", описывает подобный подход, только реализуемый по средством шаблона Декоратор. В данном случае получается еще удобней, потому что клиентам класса нет необходимости знать о том, что происходит какая-либо валидация, плюс замена правила валидации при подобной реализации пройдет наиболее безболезненно

