Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 390 Vote(s) - 3.4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to avoid "too many parameters" problem in API design?

#1
I have this API function:

public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d,
string e, string f, out Guid code)

I don't like it. Because parameter order becomes unnecessarily significant. It becomes harder to add new fields. It's harder to see what's being passed around. It's harder to refactor method into smaller parts because it creates another overhead of passing all the parameters in sub functions. Code is harder to read.

I came up with the most obvious idea: have an object encapsulating the data and pass it around instead of passing each parameter one by one. Here is what I came up with:

public class DoSomeActionParameters
{
public string A;
public string B;
public DateTime C;
public OtherEnum D;
public string E;
public string F;
}

That reduced my API declaration to:

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)

Nice. Looks very innocent but we actually introduced a huge change: we introduced mutability. Because what we previously had been doing was actually to pass an anonymous immutable object: function parameters on stack. Now we created a new class which is very mutable. We created the ability to manipulate the state of the **caller**. That sucks. Now I want my object immutable, what do I do?

public class DoSomeActionParameters
{
public string A { get; private set; }
public string B { get; private set; }
public DateTime C { get; private set; }
public OtherEnum D { get; private set; }
public string E { get; private set; }
public string F { get; private set; }

public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d,
string e, string f)
{
this.A = a;
this.B = b;
// ... tears erased the text here
}
}

As you can see I actually re-created my original problem: too many parameters. It's obvious that that's not the way to go. What am I going to do? The last option to achieve such immutability is to use a "readonly" struct like this:

public struct DoSomeActionParameters
{
public readonly string A;
public readonly string B;
public readonly DateTime C;
public readonly OtherEnum D;
public readonly string E;
public readonly string F;
}

That allows us to avoid constructors with too many parameters and achieve immutability. Actually it fixes all the problems (parameter ordering etc). Yet:

- Everybody (including FXCop & Jon Skeet) agree that [exposing public fields are bad][1].
- Eric Lippert et al say [relying on readonly fields for immutability is a lie][2].

That's when I got confused and decided to write this question: What's the most straightforward way in C# to avoid "too many parameters" problem without introducing mutability? Is it possible to use a readonly struct for that purpose and yet not have a bad API design?

**CLARIFICATIONS:**

- Please assume there is no violation of single responsibiltiy principle. In my original case the function just writes given parameters to a single DB record.
- I'm not seeking a specific solution to the given function. I'm seeking a generalized approach to such problems. I'm specifically interested in solving "too many parameters" problem without introducing mutability or a terrible design.

**UPDATE**

The answers provided here have different advantages/disadvantages. Therefore I'd like to convert this to a community wiki. I think each answer with code sample and Pros/Cons would make a good guide for similar problems in the future. I'm now trying to find out how to do it.

[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#2
What you have there is a pretty sure indication that the class in question is violating the [Single Responsibility Principle][1] because it has too many dependencies. Look for ways to refactor those dependencies into clusters of [Facade Dependencies][2].


[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#3
How about creating a builder class inside your data class. The data class will have all the setters as private and only the builder will be able to set them.



public class DoSomeActionParameters
{
public string A { get; private set; }
public string B { get; private set; }
public DateTime C { get; private set; }
public OtherEnum D { get; private set; }
public string E { get; private set; }
public string F { get; private set; }

public class Builder
{
DoSomeActionParameters obj = new DoSomeActionParameters();

public string A
{
set { obj.A = value; }
}
public string B
{
set { obj.B = value; }
}
public DateTime C
{
set { obj.C = value; }
}
public OtherEnum D
{
set { obj.D = value; }
}
public string E
{
set { obj.E = value; }
}
public string F
{
set { obj.F = value; }
}

public DoSomeActionParameters Build()
{
return obj;
}
}
}

public class Example
{

private void DoSth()
{
var data = new DoSomeActionParameters.Builder()
{
A = "",
B = "",
C = DateTime.Now,
D = testc,
E = "",
F = ""
}.Build();
}
}
Reply

#4
You could use a Builder-style approach, though depending on the complexity of your `DoSomeAction` method, this might be a touch heavyweight. Something along these lines:

public class DoSomeActionParametersBuilder
{
public string A { get; set; }
public string B { get; set; }
public DateTime C { get; set; }
public OtherEnum D { get; set; }
public string E { get; set; }
public string F { get; set; }

public DoSomeActionParameters Build()
{
return new DoSomeActionParameters(A, B, C, D, E, F);
}
}

public class DoSomeActionParameters
{
public string A { get; private set; }
public string B { get; private set; }
public DateTime C { get; private set; }
public OtherEnum D { get; private set; }
public string E { get; private set; }
public string F { get; private set; }

public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d, string e, string f)
{
A = a;
// etc.
}
}

// usage
var actionParams = new DoSomeActionParametersBuilder
{
A = "value for A",
C = DateTime.Now,
F = "I don't care for B, D and E"
}.Build();

result = foo.DoSomeAction(actionParams, out code);
Reply

#5
One style embraced in the frameworks is usually like grouping related parameters into related classes (but yet again problematic with mutability):

var request = new HttpWebRequest(a, b);
var service = new RestService(request, c, d, e);
var client = new RestClient(service, f, g);
var resource = client.RequestRestResource(); // O params after 3 objects
Reply

#6
Use the structure, but instead of public fields, have public properties:

> •Everybody (including FXCop & Jon Skeet) agree that exposing public fields are bad.

Jon and FXCop will be satisified because you are exposing properites not fields.

> •Eric Lippert et al say relying on readonly fields for immutability is a lie.

Eric will be satisifed because using properties, you can ensure that the value is only set once.


private bool propC_set=false;
private date pC;
public date C {
get{
return pC;
}
set{
if (!propC_set) {
pC = value;
}
propC_set = true;
}
}

One semi-immutable object (value can be set but not changed). Works for value and Reference types.

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through