Замена наследования на аггрегирование и фасад
От: Эйнсток Файр Мухосранск Странный реагент
Дата: 27.05.18 17:35
Оценка:
Есть ли простой синтаксис для проделывания этого (без написания тупого кода вручную и использования генераторов из библиотек для тестирования)?

т.е.

public class ObservableMyClass
{
    public void MyMethod1() { ... }
    public void MyProperty1 { get; set; }
}

public class MyClass
{
    ObservableMyClass myObject;
    public MyClass(ObservableMyClass myObject) { this.myObject = myObject; }
    public void MyMethod1() { myObject.MyMethod1(); }
    public void MyProperty1 { get {return myObject.MyProperty1;} set{myObject.MyProperty1 = value;} }
}


Не хотелось бы руками писать для каждого метода и свойства обёртку, и много внешнего кода тянуть.

Наследование мне нельзя, потому что объекты MyClass у меня связаны с объектами ObservableMyClass отношением N к 1, а не 1:1
Отредактировано 27.05.2018 17:49 Эйнсток Файр . Предыдущая версия . Еще …
Отредактировано 27.05.2018 17:48 Эйнсток Файр . Предыдущая версия .
Отредактировано 27.05.2018 17:47 Эйнсток Файр . Предыдущая версия .
Отредактировано 27.05.2018 17:36 Эйнсток Файр . Предыдущая версия .
aggregate facade decorator
Re: Замена наследования на аггрегирование и фасад
От: RushDevion Россия  
Дата: 29.05.18 06:26
Оценка: +1
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>Есть ли простой синтаксис для проделывания этого (без написания тупого кода вручную и использования генераторов из библиотек для тестирования)?


Простого нет.
Из того, что может подойти:
1. Кодогенерация через T4
2. Кодогенерация через Roslyn API
3. Свой велосипед на Code DOM или Reflection.Emit (можно взять какой-нибудь Castle Dynamic Proxy, если с нуля писать лень)
Re: Замена наследования на аггрегирование и фасад
От: Михаил Романов Удмуртия https://mihailromanov.wordpress.com/
Дата: 29.05.18 09:03
Оценка: +1
Здравствуйте, Эйнсток Файр, Вы писали:

ЭФ>Есть ли простой синтаксис для проделывания этого (без написания тупого кода вручную и использования генераторов из библиотек для тестирования)?

Как подсказал коллега RushDevion можно использовать Mixins из библиотек с DynamicProxy. Того же Castle.

Приблизительно решение выглядит так.
Пусть у нас есть 2 класса, которые мы хотим обернуть в один агрегатор:
public class ObservableMyClass1
{
    public void MyMethod1() { Console.WriteLine("1"); }
    public int MyProperty1 { get; set; }
}


public class ObservableMyClass2
{
    public void MyMethod2() { Console.WriteLine("2"); }
    public int MyProperty2 { get; set; }
}


Первым делом выделяем интерфейсы для этих классов (это можно сделать в той же студии через рефакторинг).
Т.к. нам нужно будет сохранить связь между интерфейсом и реализаций, я сделал специальный атрибут, которым разметил интерфесы (можно сделать иначе — просто искать через рефлексию классы, в которых реализуются эти интерфейсы или задать связь в коде, ... — куча вариантов).

public class ImplementationAttribute : Attribute
{
    public ImplementationAttribute(Type type)
    {
        Type = type;
    }
    public Type Type { get; set; }
}

[Implementation(typeof(ObservableMyClass1))]
public interface IObservableMyClass1
{
    int MyProperty1 { get; set; }
    void MyMethod1();
}

public class ObservableMyClass1 : IObservableMyClass1
{
    public void MyMethod1() { Console.WriteLine("1"); }
    public int MyProperty1 { get; set; }
}

[Implementation(typeof(ObservableMyClass2))]
public interface IObservableMyClass2
{
    int MyProperty2 { get; set; }
    void MyMethod2();
}

public class ObservableMyClass2 : IObservableMyClass2
{
    public void MyMethod2() { Console.WriteLine("2"); }
    public int MyProperty2 { get; set; }
}


Для того, чтобы описать агрегатор используем множественное наследование в интерфейсах
public interface IMyClass : IObservableMyClass1, IObservableMyClass2
{
}


Собственно осталось сгенерировать proxy-класс, который будет реализовывать интерфейс IMyClass, но перенаправлять вызовы на реализации интерфейсов IObservableMyClass1 и IObservableMyClass2

public T GetMixedClass<T>() where T : class
{
    var generator = new ProxyGenerator();
    var options = new ProxyGenerationOptions();

    var type = typeof(T);
    var interfaces = type.GetInterfaces();

    foreach (var interf in interfaces)
    {
        var imp = interf.GetCustomAttributes(typeof(ImplementationAttribute), true)
            .OfType<ImplementationAttribute>().SingleOrDefault();

        if (imp != null)
        {
            options.AddMixinInstance(Activator.CreateInstance(imp.Type));
        }
    }

    return (T) generator.CreateClassProxy(typeof(object), new[] { typeof(T) }, options);
}


Всё, можем пользоваться:
var a = GetMixedClass<IMyClass>();

a.MyMethod1();
a.MyMethod2();
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.