Пытаясь понять, как создавать свободные интерфейсы и когда их использовать - PullRequest
15 голосов
/ 26 мая 2011

Как можно создать свободный интерфейс вместо более традиционного подхода?Вот традиционный подход:

Интерфейс:

interface IXmlDocumentFactory<T>
{
    XmlDocument CreateXml()                    //serializes just the data
    XmlDocument CreateXml(XmlSchema schema)    //serializes data and includes schema
}

interface IXmlSchemaFactory<T>
{
    XmlSchema CreateXmlSchema()                //generates schema dynamically from type
}

Использование:

var xmlDocFactory = new XmlDocumentFactory<Foo>(foo);
var xmlDocument = xmlDocFactory.CreateXml();

//or...

var xmlDocFactory = new XmlDocumentFactory<Foo>(foo);
var xmlSchemaFactory = new XmlSchemaFactory<Foo>();
var xmlDocument = xmlDocFactory.CreateXml(xmlSchemaFactory.CreateXmlSchema());

IХотелось бы сказать:

var xmlDocument = new XmlDocumentFactory<Foo>(foo).CreateXml().IncludeSchema();
//or...
var xmlDocument = new XmlDocumentFacotry<Foo>(foo).CreateXml(); 

Наконец, подходит ли эта ситуация для свободно работающих интерфейсов?Или более традиционный подход имеет больше смысла?

Ответы [ 3 ]

8 голосов
/ 26 мая 2011

Ключ к тому, чтобы сделать интерфейс свободным, состоит в том, чтобы все методы возвращали экземпляры самого интерфейса или некоторого другого объекта, который также реализует интерфейс, который может продолжить обработку.

Так что в вашем случае каждый изваши методы IXmlDocumentFactory должны возвращать IXmlDocumentFactory, чтобы вы могли продолжать вызывать.Последний метод, если он есть, возвращает тип, который вам действительно нужен?

Он делает код очень читабельным, но еще одна вещь, которая все же дает мне немного хитрости, - это проверка возврата.Вы должны быть уверены, что нулевые значения не могут быть возвращены, иначе следующий «свободный вызов» не будет выполнен.

5 голосов
/ 26 мая 2011

Для меня важны три вещи:

1.) Существует начальный метод , который возвращает свободный интерфейс, с которым вы собираетесь работать

2.Каждый метод в классе, который реализует ваш свободный интерфейс, возвращает сам себя, чтобы вы могли продолжить цепочку - это настоящие беглые методы .

3.1012 *, который возвращает тип, который вы действительно хотите построить.

В вашем примере, поскольку у вас есть только два метода, его граница полезна - обычно у вас будет больше методов в свободном интерфейсе.В качестве альтернативы (которую я лично предпочитаю) вы можете предложить обе опции в вашем API: свободный API и более традиционный API (то есть с необязательными параметрами).

В вашем случае можно сделать что-то вроде этого:1018 * Отредактировано для ответа на комментарий.

public interface IXmlDocumentFactory<T>
{
    XmlDocument Create();                    //serializes just the data
    IXmlDocumentFactory<T> WithSchema(XmlSchema schema);    //serializes data and includes schema
}

public class DocumentFactory<T> : IXmlDocumentFactory<T>
{
    private XmlSchema _schema;
    private T _data;

    public DocumentFactory(T data)
    {
        _data = data;
    }
    public IXmlDocumentFactory<T> WithSchema(XmlSchema schema)
    {
        _schema = schema;
        return this;
    }
    public XmlDocument Create()
    {
        if (_schema != null)
        {
            //..use schema/data
            return new XmlDocument();
        }
        else //use data
            return new XmlDocument();
    }

    public static IXmlDocumentFactory<T> From(T data)
    {
        return new DocumentFactory<T>(data);
    }
}

Затем вы можете использовать его так:

var xmlDoc = DocumentFactory<int>.From(42)
                                 .WithSchema(new XmlSchema())
                                 .Create();

var xmlDoc = DocumentFactory<int>.From(42)
                                 .Create();
3 голосов
/ 26 мая 2011

ИМО, беглые API имеют свое значение.Свободно настраиваемый компонент больше напоминает английское предложение, легко читаемое от начала до конца.Намерение разработчика легче понять.

Примеры реализации приведены в таких проектах, как Autofac , Moq и Fluent NHibernate * 1008.*

...