How to avoid “too many parameters” problem in API design?

前端 未结 13 1620
别那么骄傲
别那么骄傲 2020-11-27 09:01

I have this API function:

public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d, 
     string e, string f, out Guid code)
13条回答
  •  有刺的猬
    2020-11-27 09:52

    here is a slightly different one from Mikeys but what I am trying to do is make the whole thing as little to write as possible

    public class DoSomeActionParameters
    {
        readonly string _a;
        readonly int _b;
    
        public string A { get { return _a; } }
    
        public int B{ get { return _b; } }
    
        DoSomeActionParameters(Initializer data)
        {
            _a = data.A;
            _b = data.B;
        }
    
        public class Initializer
        {
            public Initializer()
            {
                A = "(unknown)";
                B = 88;
            }
    
            public string A { get; set; }
            public int B { get; set; }
    
            public DoSomeActionParameters Create()
            {
                return new DoSomeActionParameters(this);
            }
        }
    }
    

    The DoSomeActionParameters is immutable as it can be and cannot be created directly as its default constructor is private

    The initializer is not immutable, but only a transport

    The usage takes advantage of the initializer on the Initializer (if you get my drift) And I can have defaults in the Initializer default constructor

    DoSomeAction(new DoSomeActionParameters.Initializer
                {
                    A = "Hello",
                    B = 42
                }
                .Create());
    

    The parameters will be optional here, if you want some to be required you could put them in the Initializer default constructor

    And validation could go in the Create method

    public class Initializer
    {
        public Initializer(int b)
        {
            A = "(unknown)";
            B = b;
        }
    
        public string A { get; set; }
        public int B { get; private set; }
    
        public DoSomeActionParameters Create()
        {
            if (B < 50) throw new ArgumentOutOfRangeException("B");
    
            return new DoSomeActionParameters(this);
        }
    }
    

    So now it looks like

    DoSomeAction(new DoSomeActionParameters.Initializer
                (b: 42)
                {
                    A = "Hello"
                }
                .Create());
    

    Still a little kooki I know, but going to try it anyway

    Edit: moving the create method to a static in the parameters object and adding a delegate which passes the initializer takes some of the kookieness out of the call

    public class DoSomeActionParameters
    {
        readonly string _a;
        readonly int _b;
    
        public string A { get { return _a; } }
        public int B{ get { return _b; } }
    
        DoSomeActionParameters(Initializer data)
        {
            _a = data.A;
            _b = data.B;
        }
    
        public class Initializer
        {
            public Initializer()
            {
                A = "(unknown)";
                B = 88;
            }
    
            public string A { get; set; }
            public int B { get; set; }
        }
    
        public static DoSomeActionParameters Create(Action assign)
        {
            var i = new Initializer();
            assign(i)
    
            return new DoSomeActionParameters(i);
        }
    }
    

    So the call now looks like this

    DoSomeAction(
            DoSomeActionParameters.Create(
                i => {
                    i.A = "Hello";
                })
            );
    

提交回复
热议问题