acid-chicken / Information.NET

WIP Proj: A .NET Library for all of "Information Network" services.
MIT License
5 stars 1 forks source link

The design of Common API #3

Open acid-chicken opened 6 years ago

acid-chicken commented 6 years ago

Overview

From #2. In this issue, we have a discussion about designing Information.NET.Common.

marihachi commented 6 years ago

First of all, common point is posting messages.

acid-chicken commented 6 years ago

Yeah, and in general, these are listed on timelines.

marihachi commented 6 years ago

Also, It's necessary to decide the object-name of posted text message. (e.g. Status, Message, or Text etc.)

acid-chicken commented 6 years ago

I think "Message" sounds good! How do you like to call it? @mak0tia @Siketyan

marihachi commented 6 years ago

But, when we want to define other messages, it will be probably a not good decision :thinking: If we doesn't call other objects "Message", it is ok.

marihachi commented 6 years ago

Or, How about "TextMessage"?

acid-chicken commented 6 years ago

The message contains text is not necessarily so. How about inheritance?

marihachi commented 6 years ago

sounds good!

acid-chicken commented 6 years ago

Is IMessage<T> better than inheritance?

marihachi commented 6 years ago

I think that if meaning changed by Type info, should use Generic, but, probably not. For example, what is included in T?

acid-chicken commented 6 years ago

@marihachi I've rethought that it's not a good idea.

marihachi commented 6 years ago

とりあえず、現状の構想でcommonプロジェクトを作成してみた

5

I created a common project as based on the current idea at #5 for now. — Translation added by @acid-chicken

marihachi commented 6 years ago

構想

using System.Threading.Tasks;

namespace Information
{
    public interface IService { }
    public interface IMessage { }
    public interface IMessagePostable<T> where T : IMessage
    {
        Task<T> PostMessageAsync(T message);
    }
}

namespace Information.Services
{
    public class Twitter :
        IService,
        IMessagePostable<Twitter.TextMessage>,
        IMessagePostable<Twitter.ReactionMessage>
    {
        public Task<TextMessage> PostMessageAsync(TextMessage message) => null;

        public Task<ReactionMessage> PostMessageAsync(ReactionMessage message) => null;

        public class TextMessage : IMessage
        {
            public string Content { get; set; }
        }

        public class ReactionMessage : IMessage
        {
            public ReactionType Type { get; set; }
        }

        public enum ReactionType { Like }
    }
}

public class Program
{
    public async void Main()
    {
        var tw = new Information.Services.Twitter();

        var text = new Information.Services.Twitter.TextMessage();
        text.Content = "hoge";
        await tw.PostMessageAsync(text);

        var reaction = new Information.Services.Twitter.ReactionMessage();
        reaction.Type = Information.Services.Twitter.ReactionType.Like;
        await tw.PostMessageAsync(reaction);
    }
}

どうだろう..

Conception: some codes Would you like this...? — Translation added by @acid-chicken

qpwakaba commented 6 years ago

ReactionMessage のリアクション対象の指定ってどこでやる感じです?

Where do you want to let's specify the target for the reaction of ReactionMessage? — Translation added by @acid-chicken

acid-chicken commented 6 years ago
var text = new Information.Services.Twitter.TextMessage();
text.Content = "hoge";
await tw.PostMessageAsync(text);

It looks a little bit complex.

marihachi commented 6 years ago

@acid-chicken

It looks a little bit complex.

それはそう。どのようにすれば最適だろう?

@qpwakaba

ReactionMessage のリアクション対象の指定ってどこでやる感じです?

ReactionMessageのコンストラクタもしくはプロパティから設定することになりそう

marihachi commented 6 years ago

リアクションをメッセージとして扱うのは抽象化し過ぎかなあ

marihachi commented 6 years ago

案2

メソッド名がすべてのサービスで共通になるというメリットがあるが、結局パラメータとして与えるTextMessageやReactionのような「モデル」は全てのサービスで定義して使い分けないといけない。

using System.Threading.Tasks;
using static Information.Services.Twitter;

namespace Information
{
    public interface IService { }

    public interface IMessagePostable<T> { Task PostMessageAsync(T message); }
    public interface IMessagePostable<T, TRes> { Task<TRes> PostMessageAsync(T message); }

    public interface IReactionPostable<T> { Task PostReactionAsync(T reaction); }
    public interface IReactionPostable<T, TRes> { Task<TRes> PostReactionAsync(T reaction); }
}

namespace Information.Services
{
    public class Twitter :
        IService,
        IMessagePostable<TextMessage>,
        IReactionPostable<Reaction>
    {
        public Task PostMessageAsync(TextMessage message) => null;
        public Task PostReactionAsync(Reaction reaction) => null;

        public class TextMessage
        {
            public TextMessage(string text, string inReplyToStatusId = null)
            {
                Text = text;
                InReplyToStatusId = inReplyToStatusId;
            }

            public string Text { get; set; }
            public string InReplyToStatusId { get; set; }
        }

        public class Reaction
        {
            public Reaction(ReactionType type, string targetMessageId)
            {
                Type = type;
                TargetMessageId = targetMessageId;
            }

            public ReactionType Type { get; set; }
            public string TargetMessageId { get; set; }
        }

        public enum ReactionType { Like }
    }
}

public class Program
{
    public async void Main()
    {
        var tw = new Information.Services.Twitter();

        await tw.PostMessageAsync(new TextMessage("hoge"));
        await tw.PostReactionAsync(new Reaction(ReactionType.Like, "abc"));
    }
}
acid-chicken commented 6 years ago

Here is my conception:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Information
{
    public interface IAuthored<T, TId>
        where T : class, IUnique<TId>
    {
        ICacheable<T, TId> Author { get; }
    }

    public interface ICacheable<T, TId> : IUnique<TId>
        where T : class, IUnique<TId>
    {
        T Cache { get; }
        Task<T> GetAsync(bool ignoreCache);
    }

    public interface IReactableService<T, TId, TUser, TUserId, TReaction> : IService<T>
        where T : IAuthored<TUser, TUserId>, IUnique<TId>
        where TUser : class, IUnique<TUserId>
        where TReaction : Enum
    {
        Task<IReactableMessage<T, TUser, TUserId, TReaction>> ReactAsync(IUnique<TId> target, TReaction reaction);
    }

    public interface IMessage<T>
    {
        T Content { get; }
    }

    public interface IReactableMessage<T, TUser, TUserId, TReaction> : IMessage<T>
        where TUser : class, IUnique<TUserId>
        where TReaction : Enum
    {
        ILookup<ICacheable<TUser, TUserId>, TReaction> Reactions { get; }
        Task<IReactableMessage<T, TUser, TUserId, TReaction>> ReactAsync(TReaction reaction);
    }

    public interface IRequest
    {
        // TODO
    }

    public interface IService<T>
    {
        Task<IMessage<T>> PostMessageAsync(IRequest message);
    }

    public interface IUnique<T>
    {
        T Id { get; }
    }
}