I want to reduce cyclomatic complexity of my switch case
my code is :
public String getCalenderName() {
switch (type) {
case COUNTRY:
return country == null ? name : country.getName() + HOLIDAY_CALENDAR;
case CCP:
return ccp == null ? name : ccp.getName() + " CCP" + HOLIDAY_CALENDAR;
case EXCHANGE:
return exchange == null ? name : exchange.getName() + HOLIDAY_CALENDAR;
case TENANT:
return tenant == null ? name : tenant.getName() + HOLIDAY_CALENDAR;
default:
return name;
}
}
This code blocks complexity is 16 and want to reduce it to 10.
country, ccp, exchange and tenant are my diffrent objects. Based on type I will call their respective method.
I believe it is a Sonar warning. I think Sonar warnings are not must-do-rules, but just guides. Your code block is READABLE and MAINTAINABLE as it is. It is already simple, but if you really want to change it you can try those two approaches below, and see if complexity becomes lower:
Note: I don't have compiler with me now so there can be errors, sorry about that in advance.
First approach:
Map<String, String> multipliers = new HashMap<String, Float>();
map.put("country", country);
map.put("exchange", exchange);
map.put("ccp", ccp);
map.put("tenant", tenant);
Then we can just use the map to grab the right element
return map.get(type) == null ? name : map.get(type).getName() + HOLIDAY_CALENDAR;
2nd approach:
All your objects have same method, so you can add an Interface with getName() method in it and change your method signature like:
getCalendarName(YourInterface yourObject){
return yourObject == null ? name : yourObject.getName() + HOLIDAY_CALENDAR;
}
If your first aim is only to reduce the cyclomatic complexity, you should create methods for each way of getting the name, like following.
public String getCalenderName() {
switch (type) {
case COUNTRY:
return getCountryName();
case CCP:
return getCcpName();
case EXCHANGE:
return getExchangeName();
case TENANT:
return getTenantName();
default:
return name;
}
}
private String getCountryName() {
return country == null ? name : country.getName() + HOLIDAY_CALENDAR;
}
private String getCcpName() {
return ccp == null ? name : ccp.getName() + " CCP" + HOLIDAY_CALENDAR;
}
private String getExchangeName() {
return exchange == null ? name : getName.toString() + HOLIDAY_CALENDAR;
}
private String getTenantName() {
return tenant == null ? name : getName.toString() + HOLIDAY_CALENDAR;
}
Note in your specific example, I suppose that you have 1 class that gather (at least) 4 quite similar behaviours. A refactoring would certainly make more sense, in order to have for example one base implementation (abstract or not), and 4 other inherited classes.
I think you can lower the complexity just by making sure that something else is fixed in your code.
Take the case:
case COUNTRY:
return country == null ? name : country.getName() + HOLIDAY_CALENDAR;
This implies that if the calender type is COUNTRY, the country the calender is associated with could be null. This is something you should prevent by design because I can't see a reason why this could be a valid situation. Why would you have a country calender without a country?
So make sure that there is a non-null object associated with the calender as soon as you assign a type to it. In this way your cases will be like
case COUNTRY:
return country.getName() + HOLIDAY_CALENDAR;
which lowers your cyclomatic complexity to 5.
As per my knowledge, do not use return statement in switch statement. Apply that logic after the switch statement using a variable.
Create a method for checking the null value and call that method from switch then you will able to reduce the Cyclomatic Complexity
You can remove all the null comparisons and check it prior to switch case. In that case complexity will reduce by 4 or may be more.
If your objects: country, cpp, exchange and tenant share the same interface e.g. ObjectWithGetName you could refactor your code as following:
public String getCalenderName() {
ObjectWithGetNameMethod calendarType = null;
switch (type) {
case COUNTRY:
calendarType = country;
break;
case CCP:
calendarType = cpp;
break;
case EXCHANGE:
calendarType = exchange;
break;
case TENANT:
calendarType = tenant;
break;
default:
calendarType = null;
}
return (calendarType != null ? (calendarType.getName() + HOLIDAY_CALENDAR) : name);
}
Also I think it will be nice to move switch to separate method since it looks like something witch will be used in many different places.
public String getName() {
if (type == null) {
return name;
}
if (type == BusinessCalendarType.COUNTRY) {
return country == null ? name : country.getName() + HOLIDAY_CALENDAR;
} else if (type == BusinessCalendarType.CCP) {
return ccp == null ? name : ccp.getName() + " CCP" + HOLIDAY_CALENDAR;
} else if (type == BusinessCalendarType.EXCHANGE) {
return exchange == null ? name : exchange.getName() + HOLIDAY_CALENDAR;
} else if (type == BusinessCalendarType.TENANT) {
return tenant == null ? name : tenant.getName() + HOLIDAY_CALENDAR;
} else {
return name;
}
}
this worked for me
Related
I am using key - value translation-S.of(context).translation_key- but I need something like "Translation string".translate() or translate("Translation string")
How can I do this ?
I am using localizely plugin.
Flutter version is lastest.
Attention: I am not asking for translate("key") I am asking translate("Translate String")
I want to give a value and get current translation value.
I'd suggest you to do it the other way:
String _localeCode = 'en';
Your method:
String translate(String key) {
if (_localeCode == 'en') {
if (key == 'key1') return 'English translation for key1';
else if (key == 'key2') return 'English translation for key2';
} else if (_localeCode == 'es') {
if (key == 'key1') return 'Spanish translation for key1';
else if (key == 'key2') return 'Spanish translation for key2';
}
}
And you'd use it:
void main() {
var translatedString = translate('key1');
}
For simplicity I hardcoded the things, but a better solution is to load the keys through a json file, here's a better approach
I wrote two Specifications which return null if their parameter is null.
public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {
if (linea != null) {
return (root, query, criteriaBuilder) -> {
return criteriaBuilder.like((root.join("linea")).get("nome"), "%"+linea+"%");
};
}
else return null;
}
public static Specification<Prodotto> getProdottoByIngSpec (String ing) {
if (ing != null) {
return (root, query, criteriaBuilder) -> {
return criteriaBuilder.like(((root.join("listaQuoteIng")).join("ing")).get("nome"), "%"+ing+"%");
};
}
else return null;
}
Then I created a third one that combines the previous ones with an and operator inside a where clause:
public static Specification<Prodotto> getProdottoByMainTraits (String linea, String ing) {
return Specification.where(getProdottoByLineaSpec(linea).and(getProdottoByIngSpec(ing)));
}
Now, that's the funny part:
If ByLinea returns null, i get a nullPointerException from checkPackageAccess when resolving the where clause.
If ByIng returns null, it just gets ignored (like it should be) and the query matches just the other predicate.
If I switch the two predicates, putting ByIng as the first one and then ByLinea inside the where clause, everything works in every combination.
It is a good practice to avoid returning null from methods.
You can use criteriaBuilder.conjunction() to ignore null parameter Specification. It generates always true Predicate. There is an opposite method criteriaBuilder.disjunction()
public static Specification<Prodotto> getProdottoByLineaSpec (String linea) {
return (root, query, criteriaBuilder) -> {
if (linea == null) {
return criteriaBuilder.conjunction();
}
return criteriaBuilder.like(
(root.join("linea")).get("nome"),
"%" + linea + "%"
);
}
}
P.S. You get NullPointerException if first Specification is null trying to access a method and.
To be clear it looks like this
Specification.where(null.and(getProdottoByIngSpec(ing)));
But if only second Specification is null this one works
Specification.where(getProdottoByLineaSpec(linea).and(null));
because and method parameter can be null
I am currently implementing a RESTFUL API that provides endpoints to interface with a database .
I want to implement filtering in my API , but I need to provide an endpoint that can provide a way to apply filtering on a table using all the table's columns.
I've found some patterns such as :
GET /api/ressource?param1=value1,param2=value2...paramN=valueN
param1,param2...param N being my table columns and the values.
I've also found another pattern that consists of send a JSON object that represents the query .
To filter on a field, simply add that field and its value to the query :
GET /app/items
{
"items": [
{
"param1": "value1",
"param2": "value",
"param N": "value N"
}
]
}
I'm looking for the best practice to achieve this .
I'm using EF Core with ASP.NET Core for implementing this.
Firstly be cautious about filtering on everything/anything. Base the available filters on what users will need and expand from that depending on demand. Less code to write, less complexity, fewer indexes needed on the DB side, better performance.
That said, the approach I use for pages that have a significant number of filters is to use an enumeration server side where my criteria fields are passed back their enumeration value (number) to provide on the request. So a filter field would comprise of a name, default or applicable values, and an enumeration value to use when passing an entered or selected value back to the search. The requesting code creates a JSON object with the applied filters and Base64's it to send in the request:
I.e.
{
p1: "Jake",
p2: "8"
}
The query string looks like:
.../api/customer/search?filters=XHgde0023GRw....
On the server side I extract the Base64 then parse it as a Dictionary<string,string> to feed to the filter parsing. For example given that the criteria was for searching for a child using name and age:
// this is the search filter keys, these (int) values are passed to the search client for each filter field.
public enum FilterKeys
{
None = 0,
Name,
Age,
ParentName
}
public JsonResult Search(string filters)
{
string filterJson = Encoding.UTF8.GetString(Convert.FromBase64String(filters));
var filterData = JsonConvert.DeserializeObject<Dictionary<string, string>>(filterJson);
using (var context = new TestDbContext())
{
var query = context.Children.AsQueryable();
foreach (var filter in filterData)
query = filterChildren(query, filter.Key, filter.Value);
var results = query.ToList(); //example fetch.
// TODO: Get the results, package up view models, and return...
}
}
private IQueryable<Child> filterChildren(IQueryable<Child> query, string key, string value)
{
var filterKey = parseFilterKey(key);
if (filterKey == FilterKeys.None)
return query;
switch (filterKey)
{
case FilterKeys.Name:
query = query.Where(x => x.Name == value);
break;
case FilterKeys.Age:
DateTime birthDateStart = DateTime.Today.AddYears((int.Parse(value) + 1) * -1);
DateTime birthDateEnd = birthDateStart.AddYears(1);
query = query.Where(x => x.BirthDate <= birthDateEnd && x.BirthDate >= birthDateStart);
break;
}
return query;
}
private FilterKeys parseFilterKey(string key)
{
FilterKeys filterKey = FilterKeys.None;
Enum.TryParse(key.Substring(1), out filterKey);
return filterKey;
}
You can use strings and constants to avoid the enum parsing, however I find enums are readable and keep the sent payload a little more compact. The above is a simplified example and obviously needs error checking. The implementation code for complex filter conditions such as the age to birth date above would better be suited as a separate method, but it should give you some ideas. You can search for children by name, and/or age, and/or parent's name for example.
I have invented and found it useful to combine a few filters into one type for example CommonFilters and make this type parseable from string:
[TypeConverter(typeof(CommonFiltersTypeConverter))]
public class CommonFilters
{
public PageOptions PageOptions { get; set; }
public Range<decimal> Amount { get; set; }
//... other filters
[JsonIgnore]
public bool HasAny => Amount.HasValue || PageOptions!=null;
public static bool TryParse(string str, out CommonFilters result)
{
result = new CommonFilters();
if (string.IsNullOrEmpty(str))
return false;
var parts = str.Split(new[] { ' ', ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
if (part.StartsWith("amount:") && Range<decimal>.TryParse(part.Substring(7), out Range<decimal> amount))
{
result.Amount = amount;
continue;
}
if (part.StartsWith("page-options:") && PageOptions.TryParse(part.Substring(13), out PageOptions pageOptions))
{
result.PageOptions = pageOptions;
continue;
}
//etc.
}
return result.HasAny;
}
public static implicit operator CommonFilters(string str)
{
if (TryParse(str, out CommonFilters res))
return res;
return null;
}
}
public class CommonFiltersTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string str)
{
if (CommonFilters.TryParse(str, out CommonFilters obj))
{
return obj;
}
}
return base.ConvertFrom(context, culture, value);
}
}
the request looks like this:
public class GetOrdersRequest
{
[DefaultValue("page-options:50;amount:0.001-1000;min-qty:10")]
public CommonFilters Filters { get; set; }
//...other stuff
}
In this way you reduce the number of input request parameters, especially when some queries don't care about all filters
If you use swagger map this type as string:
c.MapTypeAsString<CommonFilters>();
public static void MapTypeAsString<T>(this SwaggerGenOptions swaggerGenOptions)
{
swaggerGenOptions.MapType(typeof(T), () => new OpenApiSchema(){Type = "string"});
}
I have a grid with columns. When the grid column header is selected I post/ajax to server with header selected to return x rows.
In the following code, RefNo is integer while ProposalSectionNumber is a string.
How to make a generic function taking a string but return a Func to be used in the linq statement?
if (sort.dir == SortDirection.Asc)
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderBy(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderBy(x => x.ProposalSectionNumber);
break;
}
}
else
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderByDescending(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderByDescending(x => x.ProposalSectionNumber);
break;
}
}
I would like to do something like
string sortOrder = "RefNo"
var sortfunc = SortFunc(sortOrder)
if (sort.dir == SortDirection.Asc)
{
qry = qry.OrderBy(sortFunc)
}
else
{
qry = qry.OrderByDesc(sortFunc)
}
I have struggled creating the function SortFunc (which returns based on string or integer)
What is the best way to achieve this?
The problem with declaring a type for sortFunc is that it depends on the type of the field by which you sort. If all fields were of the same type, say, all strings, you could use the type of Expression<Func<MyEntity,string>> for your sortFunc variable.
There is another way of removing code duplication when sort fields do not share a common type. Introduce a generic helper method that takes sort order as a parameter, and call it instead of OrderBy/OrderByDescending:
private static IOrderedQueryable<T> AddOrderBy<T,TKey>(
IQueryable<T> orig
, Expression<Func<T,TKey>> selector
, bool isAscending
) {
return isAscending ? orig.OrderBy(selector) : orig.OrderByDescending(selector);
}
Now you can rewrite your code as follows:
var isAscending = (sort.dir == SortDirection.Asc);
switch (sort.field) {
case "RefNo":
qry = qry.AddOrderBy(x => x.RefNo, isAscending);
break;
case "ProposalSectionNumber":
qry = qry.AddOrderBy(x => x.ProposalSectionNumber, isAscending);
break;
}
I'm new to EntityFramework and came across a problem with an unsupported function used in the Query.
Therefore I hope the community might help to find a elegant way.
The query tries to find a user based on the username and/or the windows username.
[Flags]
public enum IdentifierType
{
Username = 1,
Windows = 2,
Any = Username | Windows,
}
The following code throws an NotSupportedException because the Enum.HasFlag is cannot be translated into a store expression.
public User GetUser(IdentifierType type, string identifier, bool loadRelations = false)
{
using (var context = contextFactory.Invoke())
{
var user = context.Users.FirstOrDefault(u => u.Username == identifier && type.HasFlag(IdentifierType.Username)
|| u.WindowsUsername == identifier && type.HasFlag(IdentifierType.Windows));
return user;
}
}
If I rewrite the query the old Fashion way, the Query works but the boolean logic is executed in the DB:
public User GetUser(IdentifierType type, string identifier, bool loadRelations = false)
{
using (var context = contextFactory.Invoke())
{
var user = context.Users.FirstOrDefault(u => u.Username == identifier && (type & IdentifierType.Username) == IdentifierType.Username
|| u.WindowsUsername == identifier && (type & IdentifierType.Windows) == IdentifierType.Windows);
return user;
}
}
WHERE ((Username = #username) AND (1 = (3 & (1)))) OR (WindowsUsername
= #windowsusername) AND (2 = (3 & (2))))
How can I force the framework to evaluate the boolean logic before it is sent to the DB, so the binary operation is not done at DB Level?
Any ideas much appreciated!
for example:
public User GetUser(IdentifierType type, string identifier, bool loadRelations = false)
{
using (var context = contextFactory.Invoke())
{
IdentifierType tw = type & IdentifierType.Windows;
IdentifierType tu = type & IdentifierType.Username;
var user = context.Users.FirstOrDefault(u => u.Username == identifier && tu == IdentifierType.Username
|| u.WindowsUsername == identifier && tw == IdentifierType.Windows);
return user;
}
}
But I hope and believe that the database server detect that 1 = (3 & (1)) is a constant
Here you go (I've converted my code to yours so may not be syntactically 100%) ...
public User GetUser(IdentifierType type, string identifier, bool loadRelations = false)
{
using (var context = contextFactory.Invoke())
{
var user = context.Users.FirstOrDefault(u => u.Username == ((type.HasFlag(IdentifierType.Username)) ? identifier : u.Username) &&
&& u.WindowsUsername == ((type.HasFlag(IdentifierType.Username)) ? identifier : u.WindowsUsername);
return user;
}
}
Sorry, I've realized that the question did not point out the effective problem. After reading more about that, I had to rewrite the question and finally got a great answer from Gert Arnold, see here.
The LINQKit provides powerful extensions like the PredicateBuilder to cover dynamic queries with OR conditions.