xml-конфигурирование
От: XopoSHiy Россия http://cleancodegame.github.io/
Дата: 12.03.10 17:18
Оценка: 27 (6) +1
Расскажу об интересном решении, которое недавно мне удалось придумать.

disclaimer: Речь на самом деле не про DI/IoC-контейнеры — это всего лишь контекст, в котором возникла задача.

Многие контейнеры поддерживают сразу два интерфейса конфигурирования — кодом/атрибутами и xml-ем.
Я пишу свой собственный DI-контейнер (самый лучший, естественно, но для данного поста это не важно). До недавнего времени его нельзя было конфигурировать через xml. Только кодом, примерно вот так:

var container = new Container(
    c =>
        {
            c.ForPlugin<IComponent>()
                .UsePluggable<Component1>()
                .UseAutoFoundPluggables()
                .DontUse<Component2>()
                .DontUse<Component3>()
                .DontUse<Component4>()
                .ReusePluggable(ReusePolicy.Never);
            c.ForPluggable<Component1>().DontUseIt();
            c.ForPluggable<Component4>()
                .ReuseIt(ReusePolicy.Never)
                .UseConstructor(typeof(IComponent))
                .Dependency("comp").UsePluggable<Component2>();
        }
    );


То есть конфигурирование — это просто последовательность вызовов методов "настрой то", "настрой сё".
Создавать для этого отдельный xml-интерфейс не хотелось. Это предполагало много тупой работы по дублированию имеющегося интерфейса, а в будущем много тупой работы по синхронизации двух интерфейсов.

Поэтому я придумал хитрый план — сделать универсальный xml-конфигуратор! Чтобы он ничего не знал про мой контейнер, а просто умел с помощью рефлексии превращать xml-элементы в вызовы методов указанного объекта.

С использованием такого конфигуратора приведенный выше пример конфигурирования можно переписать вот так:

var container = new Container(
    c => XmlConfiguration.FromXmlFile("config.xml").ApplyConfigTo(с);
    );


Вот с таким config.xml:

<?xml version="1.0" encoding="utf-8" ?>
<robocontainer>
    
    <ForPlugin pluginType="RoboContainer.Tests.Configuration.IComponent, RoboContainer.Tests">
        <UsePluggable pluggableType="RoboContainer.Tests.Configuration.Component1, RoboContainer.Tests"/>
        <UseAutoFoundPluggables/>
        <DontUse>
            <pluggableTypes item="RoboContainer.Tests.Configuration.Component2, RoboContainer.Tests"/>
            <pluggableTypes item="RoboContainer.Tests.Configuration.Component3, RoboContainer.Tests"/>
            <pluggableTypes item="RoboContainer.Tests.Configuration.Component4, RoboContainer.Tests"/>
        </DontUse>
        <ReusePluggable reusePolicy="Never"/>
    </ForPlugin>
    
    <ForPluggable pluggableType="RoboContainer.Tests.Configuration.Component1, RoboContainer.Tests">
        <DontUseIt/>
    </ForPluggable>
    
    <ForPluggable pluggableType="RoboContainer.Tests.Configuration.Component4, RoboContainer.Tests">
        <ReuseIt reusePolicy="Never"/>
        <UseConstructor>
            <argsTypes item="RoboContainer.Tests.Configuration.IComponent, RoboContainer.Tests"/>
        </UseConstructor>
        <Dependency dependencyName="comp">
            <UsePluggable pluggableType="RoboContainer.Tests.Configuration.Component2, RoboContainer.Tests"/>
        </Dependency>
    </ForPluggable>
    
</robocontainer>


Ещё раз подчеркну, что с помощью этого конфигуратора можно конфигурировать что угодно. Да хоть
Dictionary<string, int>
!

var d = new Dictionary<string, int>();
XmlConfiguration.FromXmlFile("config.xml").ApplyConfigTo(d);


<?xml version="1.0" encoding="utf-8" ?>
<dictionary>
    <Add key="key1" value="42" />
    <Add key="key2" value="3" />
    <Add key="key3" value="5" />
    <Add key="key4" value="7" />
</dictionary>


Реализация, как вы догадываетесь, совсем несложная. Но тем, что такая идея пришла мне в голову я немножко горжусь.
Впрочем, если реализация кому-то все же интересна, то вот она:
http://code.google.com/p/robo-container/source/browse/#svn/trunk/RoboContainer/RoboConfig
Она не сильно много умеет, но хорошо расширяема — можно брать за основу, если вдруг надо.

Два слова по поводу минусов. Подобное решение фактически устраняет дублирование интерфейсов (xml и кода). Как и любое дублирование, дублирование интерфейсов дает их изолированность. Иногда изолированность — это хорошее свойство (поменяли одно, второе не поломалось), иногда плохое (поменяли одно, про второе забыли). В моем случае — это единственный вид xml-конфигурирования, на который я был морально готов — никакого дублирования!
---
http://twitter.com/xoposhiy
http://xoposhiy.moikrug.ru
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.