Building a bool expressions validator
$begingroup$
I'm writing a system that's responsible for changing Status
of Order. There's a specific set of rules that define route of changing Statuses
. But, Order
and its Products
have to satisfy some requirements associated to given Status
.
For example, Let's say we have this route of status:
NEW -> ACCEPTED -> CLOSED
Status NEW
requires e.g CustomerID to be filled
Status ACCEPTED
requires: Customer ID to be filled + some other info
Status CLOSEDD
requires: Customer ID, some other info and some other info2
I came with this solution which reduces amount of if
s by huge amount, but I think it's kinda over engineered in some parts, especially throwing previous requirements in list constructor
new List>(status_predicates[Statuses.Status_Creating])
The method named Predicate
is just a helper to minimize amount of syntax.
I just realized that this:
new List<CustomPredicate<Order>>(status_predicates[Statuses.Status_Creating])
isn't going to work, so implementation changes a little bit (copying-pasting predicates from prev. status), but the concept stays the same.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Services
{
public class CustomPredicate<T>
{
public Func<T, bool> Predicate { get; set; }
public string Error { get; set; }
public CustomPredicate(Func<T, bool> predicate, string error)
{
Predicate = predicate;
Error = error;
}
}
public class StatusService
{
// dictionary of possible status change route
public static Dictionary<string, List<string>> status_routes = new Dictionary<string, List<string>>
{
{ Statuses.Status_Creating, new List<string> { Statuses.Status_SentNotConfirmed } },
{ Statuses.Status_SentNotConfirmed, new List<string> { Statuses.Status_Accepted, Statuses.Rejected } },
{ Statuses.Status_Accepted, new List<string> { Statuses.InProgress } }
};
// Dictionary of Predicates that have to be satisfied in order to "upgrade" status
// Every predicate list contains requirements of previous
// list of requirements (look at list constructor)
public static Dictionary<string, List<CustomPredicate<Order>>> status_predicates = new Dictionary<string, List<CustomPredicate<Order>>>
{
{
Statuses.Status_Creating,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
}
},
{
// In constructor there's a list of previous predicates
Statuses.Status_SentNotConfirmed,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
}
},
{
// In constructor there's a list of previous predicates
Statuses.InProgress,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
Predicate(x => x.Products.All(c => hasArrived()), "All Products still hasn't arrived yet."),
}
},
};
public static (bool Satisfies, List<string> Errors) CanStatusOfThisOrderBeChanged(Order Order, string targetStatus)
{
if (Order == null || string.IsNullOrEmpty(targetStatus))
{
return (false, new List<string> { "Order or status does not exists" });
}
var errors = new List<string>();
// here's the validator:
foreach (var CustomPredicate in status_predicates[targetStatus])
{
if (!CustomPredicate.Predicate.Invoke(Order))
{
errors.Add(CustomPredicate.Error);
}
}
return (!errors.Any(), errors);
}
public static List<string> GetAvaliableStatusesForThisOrder(Order ord)
{
if (!status_routes.ContainsKey(ord?.Status))
{
throw new Exception("Status does not exists");
}
return status_routes[ord?.Status]; // todo: checking predicates, but that's not the point of this question
}
public static CustomPredicate<Order> Predicate(Func<Order, bool> predicate, string error)
{
return new CustomPredicate<Order>(predicate, error);
}
}
}
Please do not validate real world / business logic of those bool expression, but just an approach to validate them.
c#
$endgroup$
add a comment |
$begingroup$
I'm writing a system that's responsible for changing Status
of Order. There's a specific set of rules that define route of changing Statuses
. But, Order
and its Products
have to satisfy some requirements associated to given Status
.
For example, Let's say we have this route of status:
NEW -> ACCEPTED -> CLOSED
Status NEW
requires e.g CustomerID to be filled
Status ACCEPTED
requires: Customer ID to be filled + some other info
Status CLOSEDD
requires: Customer ID, some other info and some other info2
I came with this solution which reduces amount of if
s by huge amount, but I think it's kinda over engineered in some parts, especially throwing previous requirements in list constructor
new List>(status_predicates[Statuses.Status_Creating])
The method named Predicate
is just a helper to minimize amount of syntax.
I just realized that this:
new List<CustomPredicate<Order>>(status_predicates[Statuses.Status_Creating])
isn't going to work, so implementation changes a little bit (copying-pasting predicates from prev. status), but the concept stays the same.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Services
{
public class CustomPredicate<T>
{
public Func<T, bool> Predicate { get; set; }
public string Error { get; set; }
public CustomPredicate(Func<T, bool> predicate, string error)
{
Predicate = predicate;
Error = error;
}
}
public class StatusService
{
// dictionary of possible status change route
public static Dictionary<string, List<string>> status_routes = new Dictionary<string, List<string>>
{
{ Statuses.Status_Creating, new List<string> { Statuses.Status_SentNotConfirmed } },
{ Statuses.Status_SentNotConfirmed, new List<string> { Statuses.Status_Accepted, Statuses.Rejected } },
{ Statuses.Status_Accepted, new List<string> { Statuses.InProgress } }
};
// Dictionary of Predicates that have to be satisfied in order to "upgrade" status
// Every predicate list contains requirements of previous
// list of requirements (look at list constructor)
public static Dictionary<string, List<CustomPredicate<Order>>> status_predicates = new Dictionary<string, List<CustomPredicate<Order>>>
{
{
Statuses.Status_Creating,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
}
},
{
// In constructor there's a list of previous predicates
Statuses.Status_SentNotConfirmed,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
}
},
{
// In constructor there's a list of previous predicates
Statuses.InProgress,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
Predicate(x => x.Products.All(c => hasArrived()), "All Products still hasn't arrived yet."),
}
},
};
public static (bool Satisfies, List<string> Errors) CanStatusOfThisOrderBeChanged(Order Order, string targetStatus)
{
if (Order == null || string.IsNullOrEmpty(targetStatus))
{
return (false, new List<string> { "Order or status does not exists" });
}
var errors = new List<string>();
// here's the validator:
foreach (var CustomPredicate in status_predicates[targetStatus])
{
if (!CustomPredicate.Predicate.Invoke(Order))
{
errors.Add(CustomPredicate.Error);
}
}
return (!errors.Any(), errors);
}
public static List<string> GetAvaliableStatusesForThisOrder(Order ord)
{
if (!status_routes.ContainsKey(ord?.Status))
{
throw new Exception("Status does not exists");
}
return status_routes[ord?.Status]; // todo: checking predicates, but that's not the point of this question
}
public static CustomPredicate<Order> Predicate(Func<Order, bool> predicate, string error)
{
return new CustomPredicate<Order>(predicate, error);
}
}
}
Please do not validate real world / business logic of those bool expression, but just an approach to validate them.
c#
$endgroup$
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement anAbstractValidator<Order>
for each state transition.
$endgroup$
– xander
2 hours ago
add a comment |
$begingroup$
I'm writing a system that's responsible for changing Status
of Order. There's a specific set of rules that define route of changing Statuses
. But, Order
and its Products
have to satisfy some requirements associated to given Status
.
For example, Let's say we have this route of status:
NEW -> ACCEPTED -> CLOSED
Status NEW
requires e.g CustomerID to be filled
Status ACCEPTED
requires: Customer ID to be filled + some other info
Status CLOSEDD
requires: Customer ID, some other info and some other info2
I came with this solution which reduces amount of if
s by huge amount, but I think it's kinda over engineered in some parts, especially throwing previous requirements in list constructor
new List>(status_predicates[Statuses.Status_Creating])
The method named Predicate
is just a helper to minimize amount of syntax.
I just realized that this:
new List<CustomPredicate<Order>>(status_predicates[Statuses.Status_Creating])
isn't going to work, so implementation changes a little bit (copying-pasting predicates from prev. status), but the concept stays the same.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Services
{
public class CustomPredicate<T>
{
public Func<T, bool> Predicate { get; set; }
public string Error { get; set; }
public CustomPredicate(Func<T, bool> predicate, string error)
{
Predicate = predicate;
Error = error;
}
}
public class StatusService
{
// dictionary of possible status change route
public static Dictionary<string, List<string>> status_routes = new Dictionary<string, List<string>>
{
{ Statuses.Status_Creating, new List<string> { Statuses.Status_SentNotConfirmed } },
{ Statuses.Status_SentNotConfirmed, new List<string> { Statuses.Status_Accepted, Statuses.Rejected } },
{ Statuses.Status_Accepted, new List<string> { Statuses.InProgress } }
};
// Dictionary of Predicates that have to be satisfied in order to "upgrade" status
// Every predicate list contains requirements of previous
// list of requirements (look at list constructor)
public static Dictionary<string, List<CustomPredicate<Order>>> status_predicates = new Dictionary<string, List<CustomPredicate<Order>>>
{
{
Statuses.Status_Creating,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
}
},
{
// In constructor there's a list of previous predicates
Statuses.Status_SentNotConfirmed,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
}
},
{
// In constructor there's a list of previous predicates
Statuses.InProgress,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
Predicate(x => x.Products.All(c => hasArrived()), "All Products still hasn't arrived yet."),
}
},
};
public static (bool Satisfies, List<string> Errors) CanStatusOfThisOrderBeChanged(Order Order, string targetStatus)
{
if (Order == null || string.IsNullOrEmpty(targetStatus))
{
return (false, new List<string> { "Order or status does not exists" });
}
var errors = new List<string>();
// here's the validator:
foreach (var CustomPredicate in status_predicates[targetStatus])
{
if (!CustomPredicate.Predicate.Invoke(Order))
{
errors.Add(CustomPredicate.Error);
}
}
return (!errors.Any(), errors);
}
public static List<string> GetAvaliableStatusesForThisOrder(Order ord)
{
if (!status_routes.ContainsKey(ord?.Status))
{
throw new Exception("Status does not exists");
}
return status_routes[ord?.Status]; // todo: checking predicates, but that's not the point of this question
}
public static CustomPredicate<Order> Predicate(Func<Order, bool> predicate, string error)
{
return new CustomPredicate<Order>(predicate, error);
}
}
}
Please do not validate real world / business logic of those bool expression, but just an approach to validate them.
c#
$endgroup$
I'm writing a system that's responsible for changing Status
of Order. There's a specific set of rules that define route of changing Statuses
. But, Order
and its Products
have to satisfy some requirements associated to given Status
.
For example, Let's say we have this route of status:
NEW -> ACCEPTED -> CLOSED
Status NEW
requires e.g CustomerID to be filled
Status ACCEPTED
requires: Customer ID to be filled + some other info
Status CLOSEDD
requires: Customer ID, some other info and some other info2
I came with this solution which reduces amount of if
s by huge amount, but I think it's kinda over engineered in some parts, especially throwing previous requirements in list constructor
new List>(status_predicates[Statuses.Status_Creating])
The method named Predicate
is just a helper to minimize amount of syntax.
I just realized that this:
new List<CustomPredicate<Order>>(status_predicates[Statuses.Status_Creating])
isn't going to work, so implementation changes a little bit (copying-pasting predicates from prev. status), but the concept stays the same.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Services
{
public class CustomPredicate<T>
{
public Func<T, bool> Predicate { get; set; }
public string Error { get; set; }
public CustomPredicate(Func<T, bool> predicate, string error)
{
Predicate = predicate;
Error = error;
}
}
public class StatusService
{
// dictionary of possible status change route
public static Dictionary<string, List<string>> status_routes = new Dictionary<string, List<string>>
{
{ Statuses.Status_Creating, new List<string> { Statuses.Status_SentNotConfirmed } },
{ Statuses.Status_SentNotConfirmed, new List<string> { Statuses.Status_Accepted, Statuses.Rejected } },
{ Statuses.Status_Accepted, new List<string> { Statuses.InProgress } }
};
// Dictionary of Predicates that have to be satisfied in order to "upgrade" status
// Every predicate list contains requirements of previous
// list of requirements (look at list constructor)
public static Dictionary<string, List<CustomPredicate<Order>>> status_predicates = new Dictionary<string, List<CustomPredicate<Order>>>
{
{
Statuses.Status_Creating,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
}
},
{
// In constructor there's a list of previous predicates
Statuses.Status_SentNotConfirmed,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
}
},
{
// In constructor there's a list of previous predicates
Statuses.InProgress,
new List<CustomPredicate<Order>>
{
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.CustomerNote)), "Every product of this order needs to have an customer note field filled."),
Predicate(x => x.Company != null, "You need to pick a company that will provide those products")
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty)), "Someproperty has no value"),
Predicate(x => x.Products.All(c => !string.IsNullOrWhiteSpace(c.SomeProperty2)), "Someproperty2 has no value"),
Predicate(x => !string.IsNullOrWhiteSpace(x.ContactInfo), "Contact info has to be filled"),
Predicate(x => x.Products.All(c => hasArrived()), "All Products still hasn't arrived yet."),
}
},
};
public static (bool Satisfies, List<string> Errors) CanStatusOfThisOrderBeChanged(Order Order, string targetStatus)
{
if (Order == null || string.IsNullOrEmpty(targetStatus))
{
return (false, new List<string> { "Order or status does not exists" });
}
var errors = new List<string>();
// here's the validator:
foreach (var CustomPredicate in status_predicates[targetStatus])
{
if (!CustomPredicate.Predicate.Invoke(Order))
{
errors.Add(CustomPredicate.Error);
}
}
return (!errors.Any(), errors);
}
public static List<string> GetAvaliableStatusesForThisOrder(Order ord)
{
if (!status_routes.ContainsKey(ord?.Status))
{
throw new Exception("Status does not exists");
}
return status_routes[ord?.Status]; // todo: checking predicates, but that's not the point of this question
}
public static CustomPredicate<Order> Predicate(Func<Order, bool> predicate, string error)
{
return new CustomPredicate<Order>(predicate, error);
}
}
}
Please do not validate real world / business logic of those bool expression, but just an approach to validate them.
c#
c#
edited 29 mins ago
Jamal♦
30.3k11118227
30.3k11118227
asked 18 hours ago
JoeltyJoelty
454
454
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement anAbstractValidator<Order>
for each state transition.
$endgroup$
– xander
2 hours ago
add a comment |
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement anAbstractValidator<Order>
for each state transition.
$endgroup$
– xander
2 hours ago
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement an
AbstractValidator<Order>
for each state transition.$endgroup$
– xander
2 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement an
AbstractValidator<Order>
for each state transition.$endgroup$
– xander
2 hours ago
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Short answer
Yes, it's over-engineered.
Long answer
You mention that it reduces the number of if
statements, but not why this is your goal. In fact, it's pretty clear that reducing the number of if
statements isn't inherently a target of clean code, it's something you might want to do to achieve another goal. The possibilities that come to mind are:
Enhance readibility: Your design has added a lot of code, including a new class and a helper method, and it's separated the definition of the checks away from where they're actually performed. It's a lot harder to read than if you'd just used in-line ifs.
Improve testability: If you have a combinatorial explosion of conditions, it can be useful to be able to inject in a simple condition which always passes or always fails for your unit tests, then test the actual conditions separately. But it doesn't seem like you have that issue, or that your design intends to allow that kind of injection. Really all it means is that you have more complex code, which is now harder to fully test.
So I think this makes things worse rather than better. If you want to improve readability, there's a fairly common pattern for checking preconditions which looks like:
private void Foo(int someNumber, string someText)
{
CheckPrecondition(someNumber > 0, "Numbers must be positive");
CheckPrecondition(someText != null, "Text can't be null");
// Rest of the method
}
This just requires a single helper method. No classes and no Func
s. In your case you'd also want to pass the list of errors so that the check method could add its error if there was one. Even that is additional complexity and indirection for a relatively marginal benefit, but I think it's preferable to your version.
$endgroup$
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as:Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lotCheckPrecondition
what are overloads of this method?No classes and no Funcs
What's wrong with them?
$endgroup$
– Joelty
15 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213364%2fbuilding-a-bool-expressions-validator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Short answer
Yes, it's over-engineered.
Long answer
You mention that it reduces the number of if
statements, but not why this is your goal. In fact, it's pretty clear that reducing the number of if
statements isn't inherently a target of clean code, it's something you might want to do to achieve another goal. The possibilities that come to mind are:
Enhance readibility: Your design has added a lot of code, including a new class and a helper method, and it's separated the definition of the checks away from where they're actually performed. It's a lot harder to read than if you'd just used in-line ifs.
Improve testability: If you have a combinatorial explosion of conditions, it can be useful to be able to inject in a simple condition which always passes or always fails for your unit tests, then test the actual conditions separately. But it doesn't seem like you have that issue, or that your design intends to allow that kind of injection. Really all it means is that you have more complex code, which is now harder to fully test.
So I think this makes things worse rather than better. If you want to improve readability, there's a fairly common pattern for checking preconditions which looks like:
private void Foo(int someNumber, string someText)
{
CheckPrecondition(someNumber > 0, "Numbers must be positive");
CheckPrecondition(someText != null, "Text can't be null");
// Rest of the method
}
This just requires a single helper method. No classes and no Func
s. In your case you'd also want to pass the list of errors so that the check method could add its error if there was one. Even that is additional complexity and indirection for a relatively marginal benefit, but I think it's preferable to your version.
$endgroup$
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as:Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lotCheckPrecondition
what are overloads of this method?No classes and no Funcs
What's wrong with them?
$endgroup$
– Joelty
15 hours ago
add a comment |
$begingroup$
Short answer
Yes, it's over-engineered.
Long answer
You mention that it reduces the number of if
statements, but not why this is your goal. In fact, it's pretty clear that reducing the number of if
statements isn't inherently a target of clean code, it's something you might want to do to achieve another goal. The possibilities that come to mind are:
Enhance readibility: Your design has added a lot of code, including a new class and a helper method, and it's separated the definition of the checks away from where they're actually performed. It's a lot harder to read than if you'd just used in-line ifs.
Improve testability: If you have a combinatorial explosion of conditions, it can be useful to be able to inject in a simple condition which always passes or always fails for your unit tests, then test the actual conditions separately. But it doesn't seem like you have that issue, or that your design intends to allow that kind of injection. Really all it means is that you have more complex code, which is now harder to fully test.
So I think this makes things worse rather than better. If you want to improve readability, there's a fairly common pattern for checking preconditions which looks like:
private void Foo(int someNumber, string someText)
{
CheckPrecondition(someNumber > 0, "Numbers must be positive");
CheckPrecondition(someText != null, "Text can't be null");
// Rest of the method
}
This just requires a single helper method. No classes and no Func
s. In your case you'd also want to pass the list of errors so that the check method could add its error if there was one. Even that is additional complexity and indirection for a relatively marginal benefit, but I think it's preferable to your version.
$endgroup$
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as:Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lotCheckPrecondition
what are overloads of this method?No classes and no Funcs
What's wrong with them?
$endgroup$
– Joelty
15 hours ago
add a comment |
$begingroup$
Short answer
Yes, it's over-engineered.
Long answer
You mention that it reduces the number of if
statements, but not why this is your goal. In fact, it's pretty clear that reducing the number of if
statements isn't inherently a target of clean code, it's something you might want to do to achieve another goal. The possibilities that come to mind are:
Enhance readibility: Your design has added a lot of code, including a new class and a helper method, and it's separated the definition of the checks away from where they're actually performed. It's a lot harder to read than if you'd just used in-line ifs.
Improve testability: If you have a combinatorial explosion of conditions, it can be useful to be able to inject in a simple condition which always passes or always fails for your unit tests, then test the actual conditions separately. But it doesn't seem like you have that issue, or that your design intends to allow that kind of injection. Really all it means is that you have more complex code, which is now harder to fully test.
So I think this makes things worse rather than better. If you want to improve readability, there's a fairly common pattern for checking preconditions which looks like:
private void Foo(int someNumber, string someText)
{
CheckPrecondition(someNumber > 0, "Numbers must be positive");
CheckPrecondition(someText != null, "Text can't be null");
// Rest of the method
}
This just requires a single helper method. No classes and no Func
s. In your case you'd also want to pass the list of errors so that the check method could add its error if there was one. Even that is additional complexity and indirection for a relatively marginal benefit, but I think it's preferable to your version.
$endgroup$
Short answer
Yes, it's over-engineered.
Long answer
You mention that it reduces the number of if
statements, but not why this is your goal. In fact, it's pretty clear that reducing the number of if
statements isn't inherently a target of clean code, it's something you might want to do to achieve another goal. The possibilities that come to mind are:
Enhance readibility: Your design has added a lot of code, including a new class and a helper method, and it's separated the definition of the checks away from where they're actually performed. It's a lot harder to read than if you'd just used in-line ifs.
Improve testability: If you have a combinatorial explosion of conditions, it can be useful to be able to inject in a simple condition which always passes or always fails for your unit tests, then test the actual conditions separately. But it doesn't seem like you have that issue, or that your design intends to allow that kind of injection. Really all it means is that you have more complex code, which is now harder to fully test.
So I think this makes things worse rather than better. If you want to improve readability, there's a fairly common pattern for checking preconditions which looks like:
private void Foo(int someNumber, string someText)
{
CheckPrecondition(someNumber > 0, "Numbers must be positive");
CheckPrecondition(someText != null, "Text can't be null");
// Rest of the method
}
This just requires a single helper method. No classes and no Func
s. In your case you'd also want to pass the list of errors so that the check method could add its error if there was one. Even that is additional complexity and indirection for a relatively marginal benefit, but I think it's preferable to your version.
answered 15 hours ago
Ben AaronsonBen Aaronson
5,4841538
5,4841538
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as:Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lotCheckPrecondition
what are overloads of this method?No classes and no Funcs
What's wrong with them?
$endgroup$
– Joelty
15 hours ago
add a comment |
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as:Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lotCheckPrecondition
what are overloads of this method?No classes and no Funcs
What's wrong with them?
$endgroup$
– Joelty
15 hours ago
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as: Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lot CheckPrecondition
what are overloads of this method? No classes and no Funcs
What's wrong with them?$endgroup$
– Joelty
15 hours ago
$begingroup$
It's a lot harder to read than if you'd just used in-line ifs.
I agree, but I thought about it as: Ok, so I have e.g 8 different statuses and every of them has at least 5 checks, so I have switch with 8 cases + 8 methods which contain 5+ ifs
- it's a lot CheckPrecondition
what are overloads of this method? No classes and no Funcs
What's wrong with them?$endgroup$
– Joelty
15 hours ago
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213364%2fbuilding-a-bool-expressions-validator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
$begingroup$
The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How to Ask for examples, and revise the title accordingly.
$endgroup$
– Zeta
17 hours ago
$begingroup$
Can you post other types too so that the code compiles without errors?
$endgroup$
– t3chb0t
13 hours ago
$begingroup$
Are you open to using 3rd-party libraries? What you're building looks a lot like FluentValidation to me--just implement an
AbstractValidator<Order>
for each state transition.$endgroup$
– xander
2 hours ago