E
E
Eugene-1232017-07-20 12:11:43
C++ / C#
Eugene-123, 2017-07-20 12:11:43

Implementing a Property as an Object: Pattern or Anti-Pattern?

It is necessary to pack aspect operations like validation, logging, cancel/resume in an object behaving like a property. I don't want to use PostSharp. Below is my implementation (not ideal, but just for example) in the form of the ExtensionProperty class for primitives. The question actually is whether this method is suitable for AOP. Does it make sense to refine the type system (such as in code) or is it better to use frameworks. There are a lot of disadvantages in the implementation, but the main general question remains: Does it make sense to implement a property as an object?

Code in C#
class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Age.Value = 100;
            person.BankAccountId.Value = 125;
            person.BankAccountId.Value = -50;
            Console.WriteLine(person);
            Console.ReadLine();
        }
    }

    // Использую данный класс для создания списка шаблонных объектов от различных типов.
    public abstract class ExtendedProperty : INotifyPropertyChanged
    {
        protected string description;

        public string Description => description;

        // Метод set доступен всем - нарушаем инкапсуляцию, но сейчас меня это не волнует.
        public abstract object Value { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // Объект имитирующий свойство.
    // Содержит описание свойства. Производит валидацию, логирование.
    // На данный момент только для примитивов.
    public class ExtendedProperty<T> : ExtendedProperty where T : struct
    {
        private T _value;

        private readonly bool mutable;

        public Func<T, bool> Validator;
        
        public ExtendedProperty(string description, T initialValue, bool mutable = true)
        {
            this.description = description;
            this.mutable = mutable;
            _value = initialValue;
        }

        private void setValue(T value)
        {
            if (mutable)
            {
                if (Validator != null)
                {
                    if (!Validator(value))
                    {
                        Debug.WriteLine($"Wrong assignment | {description}, Value = {value}");
                        return;
                    }
                }
                if (!(_value.Equals(value)))
                {
                    Debug.WriteLine($"Modified | {description}, {_value} -> {value}");
                    _value = value;
                    OnPropertyChanged(nameof(Value));
                }
            }
            else
            {
                Debug.WriteLine($"Attempt to modify immutable value! | {Description}");
            }
        }

        private T getValue()
        {
            return _value;
        }

        public override object Value
        {
            get { return getValue(); }
            set { setValue((T)value); }
        }

        public void Undo() { /* На данный момент не реализовано. */ }

        public void Redo() { /* На данный момент не реализовано. */ }

        public override string ToString()
        {
            return $"\n[{Description} : {Value}]";
        }
    }

    public class Person
    {
        // Теперь не надо прописывать геттеры, сеттеры с логированием, OnPropertyChanged и прочей 
        // раздражающей глаза, но необходимой ересью. 
        // Теперь только лямбду для валидации, и то не во всех случаях.
        // Пока класс Person, не может узнает об изменениях (потом доработаю).
        // Объект будет представлять из себя совокупность ExtendedProperties (правда, с нарушенной инкапсуляцией). 
        // Вопрос только в том не обернется ли это адом в последующем?
        public ExtendedProperty<int> Age = new ExtendedProperty<int>("Person age", 0)
        {
            Validator = (age) => ((age >= 0) && (age <= 120))
        };

        public ExtendedProperty<int> BankAccountId = new ExtendedProperty<int>("Bank Account Id", 0)
        {
            Validator = (id) => (id >= 0)
        };

        public override string ToString()
        {
            return $"{Age}{BankAccountId}";
        }
    }

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question