Попытка понять, как создавать свободные интерфейсы, и когда их использовать

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

Интерфейс:

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());

Я хотел бы сказать:

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

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

Ответы

Ответ 1

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

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

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

Ответ 2

В моем сознании важны три вещи:

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

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

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

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

В вашем случае будет что-то вроде этого:

Отредактировано для ответа на комментарий.

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

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

Для примеров реализации, пожалуйста, обратитесь к таким проектам, как Autofac, Moq и Fluent NHibernate