-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add compositional delegates example #1
base: master
Are you sure you want to change the base?
Conversation
6a027f6
to
1a10808
Compare
Looks very similar to the RouteHandler class I have apart from it uses local functions rather than delegates? Thanks! |
Yeah, the RouteHandler class does the same job as my The main difference is below:
This is your application logic, but the interface In the example I provided, we use currying to make it explicit which are the dependencies and which are the parameters that will be different for each invocation:
I prefer this approach because:
You have moved this responsibility into a higher level of the application - anyone using the function has to know which parameters should always be the same value and which are dynamic:
The result is the same - a delegate that takes a film and returns nothing. But the knowledge of knowing that a ValidUserDelegate is a constructor parameter is now duplicated in two places. Whereas in the pattern I showed, you can only use the function in the expected way. You have to pass in the constructor parameter to construct the function:
|
I don't have an interface though 😄 |
But I see what you're saying this is the interface - https://github.com/jchannon/T1000/pull/1/files#diff-c7f0cf9e2bf799fe35a78a4ce3468183R69 And the implementation is - https://github.com/jchannon/T1000/pull/1/files#diff-c7f0cf9e2bf799fe35a78a4ce3468183R72 |
@jchannon Yeah, the delegate is the top-level interface called by your 'controller'. By interface I meant the signature of your function which takes a validUserQuery and a Film. That interface (interface in the general term https://stackoverflow.com/questions/2866987/what-is-the-definition-of-interface-in-object-oriented-programming) implies that to create a film you pass in a film and you pass in a valid user query and those two arguments are different for each invocation of the function. Really, the valid user query is a collaborator that the function depends on, so it doesn't need to be passed into each invocation. You've made that clear in your |
If we were to consider these two as objects, what I'm proposing is:
^^^ The object itself owns the rules of deciding which dependencies are collaborators via the constructor and which parameters values can vary on each invocation of the function Your base example is like this:
In this example, the object itself is just the raw function, and it's upto consumers to decide how to curry the arguments. There's more flexibility here - you can pass a different ValidUserQuery into each invocation - that's what the signature is saying here - both of the these parameters must be supplied on each invocation. But if your intention is for ValidUserQuery to be a collaborator that is used by every invocation of the object, anyone using your CreatFilm function has to remember this. It's not right or wrong, it's just about making your intentions clear and making the compiler enforce them for you. |
Yup, cheers big ears!
…On 1 May 2018 at 10:01, Nick ***@***.***> wrote:
If we were to consider these two as objects, what I'm proposing is:
public class CreateFilm
{
public CreateFilm(ValidateUser validate)
{
...
}
public void Create(Film film)
{
...
}
}
^^^ The object itself owns the rules of deciding which dependencies are
collaborators via the constructor and which parameters should change on
each invocation of the function
Your base example is like this:
public class CreateFilm
{
public Action<Film, ValidUserQuery> Create()
{
...
}
}
In this example, the object itself is just the raw function, and it's upto
consumers to decide how to curry the arguments. There's more flexibility
here - you can pass a different ValidUserQuery into each invocation -
that's what the signature is saying here - both of the these parameters
must be supplied on each invocation.
But if your intention is for ValidUserQuery to be a collaborator that is
used by every invocation of the object, anyone using your CreatFilm
function has to remember this.
It's not right or wrong, it's just about making your intentions clear and
making the compiler enforce them for you.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAGaprHtFxGVKA9MZ8WW_f7lv9KUt62vks5tuCRygaJpZM4Tmzp7>
.
|
Hi @jchannon,
I love what you're doing here. This PR contains an example of the patterns I've used in the past - both in C# and Scala / Play Framework. It uses basic functional techniques: currying to simulate object constructors, and returning functions from functions. With C#s local functions, the syntax is quite neat, too.
Like you, I found I got the same benefits - loosely-coupled, testable code, yet with none of the class, interface, and mocking noisy syntax so it was much easier to read and maintain.