programing

유창한 API 만들기

nicescript 2021. 1. 18. 07:39
반응형

유창한 API 만들기


본질적으로 유창한 API를 만드는 방법은 무엇입니까?

이것은 주로 확장 방법을 사용합니까?


이 기사 는 내가 할 수 있었던 것보다 훨씬 더 잘 설명합니다.

편집, 주석에서 이것을 짜낼 수 없습니다 ...

인터페이스에는 두 가지 측면, 구현 및 사용이 있습니다. 생성 측면에서해야 할 일이 더 많지만 동의합니다. 그러나 주요 이점은 사물의 사용 측면에서 찾을 수 있습니다. 사실, 유창한 인터페이스의 가장 큰 장점은 더 자연스럽고 기억하고 사용하기 쉬우 며 더 심미적으로 만족스러운 API입니다. 그리고 유창한 형태로 API를 짜 내야하는 노력이 API를 더 잘 생각하게 만들 수 있을까요?

Martin Fowler 가 유창한 인터페이스대한 원본 기사 에서 말했듯이 :

아마도이 스타일에 대해 알아 두어야 할 가장 중요한 점은 내부 DomainSpecificLanguage의 라인을 따라 무언가를 수행하는 것입니다. 실제로 이것이 우리가 그것을 설명하기 위해 '유창함'이라는 용어를 선택한 이유입니다. 여러면에서 두 용어는 동의어입니다. API는 주로 읽기 쉽고 흐름이 가능하도록 설계되었습니다. 이 유창함의 대가는 생각과 API 구성 자체 모두에서 더 많은 노력입니다. 생성자, setter 및 추가 메소드의 간단한 API는 작성하기가 훨씬 쉽습니다. 멋지고 유창한 API를 만들려면 약간의 생각이 필요합니다.

대부분의 경우 API는 한 번 생성되고 반복적으로 사용되므로 추가 노력이 그만한 가치가 있습니다.

그리고 장황? 나는 그것이 프로그램의 가독성을 제공한다면 모든 것을 자세히 설명합니다.


MrBlah,

유창한 인터페이스를 작성하기 위해 확장 메서드를 작성할 수 있지만 더 나은 접근 방식은 빌더 패턴을 사용하는 것입니다. 나는 당신과 같은 배에 있고 유창한 인터페이스의 몇 가지 고급 기능을 파악하려고 노력하고 있습니다.

아래에는 다른 스레드 에서 만든 몇 가지 샘플 코드가 있습니다.

public class Coffee
{
    private bool _cream;
    private int _ounces;

    public static Coffee Make { get { return new Coffee(); } }

    public Coffee WithCream()
    {
        _cream = true;
        return this;
    }
    public Coffee WithOuncesToServe(int ounces)
    {
        _ounces = ounces;
        return this;
    }
}

var myMorningCoffee = Coffee.Make.WithCream().WithOuncesToServe(16);

많은 사람들이 Martin Fowler를 유창한 API 토론에서 두드러진 지수로 언급하지만, 그의 초기 디자인 주장은 실제로 유창한 빌더 패턴 또는 메소드 체인을 중심으로 진화 합니다. Fluent API는 실제 내부 도메인 별 언어 로 더욱 발전 할 수 있습니다 . 문법의 BNF 표기법을 "유창한 API"로 수동으로 변환하는 방법을 설명하는 기사는 여기에서 볼 수 있습니다.

http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/

이 문법을 변형합니다.

여기에 이미지 설명 입력

이 Java API에 :

// Initial interface, entry point of the DSL
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {
  void end();
}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow
// for repetitions. Repetitions can be ended any time because this 
// interface extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

Java와 C #은 다소 유사하지만 예제는 확실히 사용 사례로도 변환됩니다. 위의 기술은 Java에서 SQL 언어를 모델링하는 유창한 API / 내부 도메인 별 언어 인 jOOQ 에서 많이 사용되었습니다.


이것은 매우 오래된 질문이고,이 답변은 아마도 답변 이라기보다는 코멘트 여야하지만, 계속해서 이야기 할 가치가있는 주제라고 생각하며,이 답변은 코멘트가 되기에는 너무 깁니다.

"유창성"에 대한 원래의 생각은 기본적으로 코드를 좀 더 자명하게 만들면서 객체에 힘과 유연성 (메서드 체인 등)을 추가하는 것입니다.

예를 들면

Company a = new Company("Calamaz Holding Corp");
Person p = new Person("Clapper", 113, 24, "Frank");
Company c = new Company(a, 'Floridex', p, 1973);

보다 덜 "유창"하다

Company c = new Company().Set
    .Name("Floridex");
    .Manager(
        new Person().Set.FirstName("Frank").LastName("Clapper").Awards(24)
    )
    .YearFounded(1973)
    .ParentCompany(
        new Company().Set.Name("Calamaz Holding Corp")
    )
;

But to me, the later is not really any more powerful or flexible or self-explanatory than

Company c = new Company(){
   Name = "Floridex",
   Manager = new Person(){ FirstName="Frank", LastName="Clapper", Awards=24 },
   YearFounded = 1973,
   ParentCompany = new Company(){ Name="Calamaz Holding Corp." }
};

..in fact I would call this last version easier to create, read and maintain than the previous, and I would say that it requires significantly less baggage behind the scenes, as well. Which to me is important, for (at least) two reasons:

1 - The cost associated with creating and maintaining layers of objects (no matter who does it) is just as real, relevant and important as the cost associated with creating and maintaining the code that consumes them.

2 - Code bloat embedded in layers of objects creates just as many (if not more) problems as code bloat in the code that consumes those objects.

Using the last version means you can add a (potentially useful) property to the Company class simply by adding one, very simple line of code.

That's not to say that I feel there's no place for method chaining. I really like being able to do things like (in JavaScript)

var _this = this;
Ajax.Call({
    url: '/service/getproduct',
    parameters: {productId: productId},
)
.Done(
    function(product){
        _this.showProduct(product);
    }
)
.Fail(
    function(error){
        _this.presentError(error);
    }
);

..where (in the hypothetical case I'm imagining) Done and Fail were additions to the original Ajax object, and were able to be added without changing any of the original Ajax object code or any of the existing code that made use of the original Ajax object, and without creating one-off things that were exceptions to the general organization of the code.

So I have definitely found value in making a subset of an object's functions return the 'this' object. In fact whenever I have a function that would otherwise return void, I consider having it return this.

But I haven't yet really found significant value in adding "fluent interfaces" (.eg "Set") to an object, although theoretically it seems like there could be a sort of namespace-like code organization that could arise out of the practice of doing that, which might be worthwhile. ("Set" might not be particularly valuable, but "Command", "Query" and "Transfer" might, if it helped organize things and facilitate and minimize the impact of additions and changes.) One of the potential benefits of such a practice, depending on how it was done, might be improvement in a coder's typical level of care and attention to protection levels - the lack of which has certainly caused great volumes grief.


KISS: Keep it simple stupid.

Fluent design is about one aesthetic design principle used throughout the API. Thou your methodology you use in your API can change slightly, but it is generally better to stay consistent.

Even though you may think 'everyone can use this API, because it uses all different types of methodology's'. The truth is the user would start feeling lost because your consistently changing the structure/data structure of the API to a new design principle or naming convention.

If you wish to change halfway through to a different design principle eg.. Converting from error codes to exception handling because some higher commanding power. It would be folly and would normally in tail lots of pain. It is better to stay the course and add functionality that your customers can use and sell than to get them to re-write and re-discover all their problems again.

Following from the above, you can see that there is more at work of writing a Fluent API than meet's the eye. There are psychological, and aesthetic choices to make before beginning to write one and even then the feeling,need, and desire to conform to customers demand's and stay consistent is the hardest of all.


What is a fluent API

Wikipedia defines them here http://en.wikipedia.org/wiki/Fluent_interface

Why Not to use a fluent interface

I would suggest not implementing a traditional fluent interface, as it increases the amount of code you need to write, complicates your code and is just adding unnecessary boilerplate.

Another option, do nothing!

Don't implement anything. Don't provide "easy" constructors for setting properties and don't provide a clever interface to help your client. Allow the client to set the properties however they normally would. In .Net C# or VB this could be as simple as using object initializers.

Car myCar = new Car { Name = "Chevrolet Corvette", Color = Color.Yellow };

So you don't need to create any clever interface in your code, and this is very readable.

If you have very complex Sets of properties which must be set, or set in a certain order, then use a separate configuration object and pass it to the class via a separate property.

CarConfig conf = new CarConfig { Color = Color.Yellow, Fabric = Fabric.Leather };
Car myCar = new Car { Config = conf };

With a fluent API:

myCar.SetColor(Color.Blue).SetName("Aston Martin");

Check out this video http://www.viddler.com/explore/dcazzulino/videos/8/


Writting a fluent API it's complicated, that's why I've written Diezel that is a Fluent API generator for Java. It generates the API with interfaces (or course) to:

  1. control the calling flow
  2. catch generic types (like guice one)

It generates also implementations.

It's a maven plugin.


No and yes. The basics are a good interface or interfaces for the types that you want to behave fluently. Libraries with extension methods can extend this behavior and return the interface. Extension methods give others the possibility to extend your fluent API with more methods.

유창한 디자인은 어려울 수 있으며 기본 구성 요소를 완전히 미세 조정하는 데 다소 오랜 시행 착오가 필요합니다. 구성 또는 설정을위한 유창한 API는 그렇게 어렵지 않습니다.

유창한 API 구축을 배우는 것은 기존 API를 살펴 보는 것입니다. FluentNHibernate를 유창한 .NET API 또는 ICriteria 유창 인터페이스와 비교하십시오. 많은 구성 API도 "유창하게"설계되었습니다.

참조 URL : https://stackoverflow.com/questions/1622662/creating-api-that-is-fluent

반응형