Elegant way to initialize from one of two values of the same type

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
1
down vote

favorite
1












I have a class that contains 2 properties of the same type. decimal NetAmount and decimal GrossAmount



I would like to initialize it using either GrossAmount or NetAmount and based on the one specified calculate the second one.



Which way is the most elegant and why? (parameter validation is omitted for brevity)



1



public class TaxedPrice

private TaxedPrice()


public decimal NetAmount get; private set;
public decimal GrossAmount get; private set;

public static TaxedPrice FromNet(decimal netAmount, decimal taxRate)

return new TaxedPrice

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero),
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero)
;


public static TaxedPrice FromGross(decimal grossAmount, decimal taxRate)

return new TaxedPrice

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero),
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero)
;




2



public class TaxedPrice

public TaxedPrice(decimal netAmount, decimal grossAmount, decimal taxRate)

if (grossAmount != default)

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (netAmount != default)

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

throw new InvalidOperationException($"Either nameof(netAmount) or grossAmount must be set.");



public decimal NetAmount get;
public decimal GrossAmount get;



3



public class TaxedPrice

public enum Type

Gross,
Net


public TaxedPrice(decimal amount, Type type, decimal taxRate)

if (type == Type.Gross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (type == Type.Net)

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



4



public class TaxedPrice

public TaxedPrice(decimal amount, bool fromGross, decimal taxRate)

if (fromGross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



How it looks like from caller's side:



// 1
var taxedPrice = TaxedPrice.FromNet(2.123m, 0.23m);
// 2
var taxedPrice = new TaxedPrice(2.123m, default, 0.23m); // uses the first one to calculate the second one
var taxedPrice2 = new TaxedPrice(2.123m, 1.11m, 0.23m); // uses the first one to calculate the second one
var taxedPrice3 = new TaxedPrice(default, 1.11m, 0.23m); // uses the second one to calculate the first one
// 3
var taxedPrice = new TaxedPrice(2.123m, TaxedPrice.Type.Net, 0.23m);
// 4
var taxedPrice = new TaxedPrice(2.123m, false, 0.23m);


Extensions for tax:



public static class TaxExtensions

public static decimal ApplyTax(this decimal netPrice, decimal taxRate)

return netPrice * (taxRate + 1);


public static decimal RemoveTax(this decimal grossPrice, decimal taxRate)

return grossPrice / (taxRate + 1);




In my upper layer I pass those prices in POCOs/DTOs like:



public class PriceDTO

public decimal NetAmount get; set;
public decimal GrossAmount get; set;



And I have to check there as well which one was passed to decide from which to calculate.










share|improve this question























  • There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
    – t3chb0t
    1 hour ago










  • @t3chb0t simple extensions for decimal type that add/remove tax from the price.
    – Konrad
    1 hour ago











  • ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
    – t3chb0t
    59 mins ago






  • 1




    @t3chb0t added code
    – Konrad
    56 mins ago










  • oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
    – t3chb0t
    52 mins ago














up vote
1
down vote

favorite
1












I have a class that contains 2 properties of the same type. decimal NetAmount and decimal GrossAmount



I would like to initialize it using either GrossAmount or NetAmount and based on the one specified calculate the second one.



Which way is the most elegant and why? (parameter validation is omitted for brevity)



1



public class TaxedPrice

private TaxedPrice()


public decimal NetAmount get; private set;
public decimal GrossAmount get; private set;

public static TaxedPrice FromNet(decimal netAmount, decimal taxRate)

return new TaxedPrice

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero),
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero)
;


public static TaxedPrice FromGross(decimal grossAmount, decimal taxRate)

return new TaxedPrice

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero),
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero)
;




2



public class TaxedPrice

public TaxedPrice(decimal netAmount, decimal grossAmount, decimal taxRate)

if (grossAmount != default)

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (netAmount != default)

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

throw new InvalidOperationException($"Either nameof(netAmount) or grossAmount must be set.");



public decimal NetAmount get;
public decimal GrossAmount get;



3



public class TaxedPrice

public enum Type

Gross,
Net


public TaxedPrice(decimal amount, Type type, decimal taxRate)

if (type == Type.Gross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (type == Type.Net)

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



4



public class TaxedPrice

public TaxedPrice(decimal amount, bool fromGross, decimal taxRate)

if (fromGross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



How it looks like from caller's side:



// 1
var taxedPrice = TaxedPrice.FromNet(2.123m, 0.23m);
// 2
var taxedPrice = new TaxedPrice(2.123m, default, 0.23m); // uses the first one to calculate the second one
var taxedPrice2 = new TaxedPrice(2.123m, 1.11m, 0.23m); // uses the first one to calculate the second one
var taxedPrice3 = new TaxedPrice(default, 1.11m, 0.23m); // uses the second one to calculate the first one
// 3
var taxedPrice = new TaxedPrice(2.123m, TaxedPrice.Type.Net, 0.23m);
// 4
var taxedPrice = new TaxedPrice(2.123m, false, 0.23m);


Extensions for tax:



public static class TaxExtensions

public static decimal ApplyTax(this decimal netPrice, decimal taxRate)

return netPrice * (taxRate + 1);


public static decimal RemoveTax(this decimal grossPrice, decimal taxRate)

return grossPrice / (taxRate + 1);




In my upper layer I pass those prices in POCOs/DTOs like:



public class PriceDTO

public decimal NetAmount get; set;
public decimal GrossAmount get; set;



And I have to check there as well which one was passed to decide from which to calculate.










share|improve this question























  • There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
    – t3chb0t
    1 hour ago










  • @t3chb0t simple extensions for decimal type that add/remove tax from the price.
    – Konrad
    1 hour ago











  • ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
    – t3chb0t
    59 mins ago






  • 1




    @t3chb0t added code
    – Konrad
    56 mins ago










  • oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
    – t3chb0t
    52 mins ago












up vote
1
down vote

favorite
1









up vote
1
down vote

favorite
1






1





I have a class that contains 2 properties of the same type. decimal NetAmount and decimal GrossAmount



I would like to initialize it using either GrossAmount or NetAmount and based on the one specified calculate the second one.



Which way is the most elegant and why? (parameter validation is omitted for brevity)



1



public class TaxedPrice

private TaxedPrice()


public decimal NetAmount get; private set;
public decimal GrossAmount get; private set;

public static TaxedPrice FromNet(decimal netAmount, decimal taxRate)

return new TaxedPrice

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero),
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero)
;


public static TaxedPrice FromGross(decimal grossAmount, decimal taxRate)

return new TaxedPrice

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero),
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero)
;




2



public class TaxedPrice

public TaxedPrice(decimal netAmount, decimal grossAmount, decimal taxRate)

if (grossAmount != default)

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (netAmount != default)

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

throw new InvalidOperationException($"Either nameof(netAmount) or grossAmount must be set.");



public decimal NetAmount get;
public decimal GrossAmount get;



3



public class TaxedPrice

public enum Type

Gross,
Net


public TaxedPrice(decimal amount, Type type, decimal taxRate)

if (type == Type.Gross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (type == Type.Net)

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



4



public class TaxedPrice

public TaxedPrice(decimal amount, bool fromGross, decimal taxRate)

if (fromGross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



How it looks like from caller's side:



// 1
var taxedPrice = TaxedPrice.FromNet(2.123m, 0.23m);
// 2
var taxedPrice = new TaxedPrice(2.123m, default, 0.23m); // uses the first one to calculate the second one
var taxedPrice2 = new TaxedPrice(2.123m, 1.11m, 0.23m); // uses the first one to calculate the second one
var taxedPrice3 = new TaxedPrice(default, 1.11m, 0.23m); // uses the second one to calculate the first one
// 3
var taxedPrice = new TaxedPrice(2.123m, TaxedPrice.Type.Net, 0.23m);
// 4
var taxedPrice = new TaxedPrice(2.123m, false, 0.23m);


Extensions for tax:



public static class TaxExtensions

public static decimal ApplyTax(this decimal netPrice, decimal taxRate)

return netPrice * (taxRate + 1);


public static decimal RemoveTax(this decimal grossPrice, decimal taxRate)

return grossPrice / (taxRate + 1);




In my upper layer I pass those prices in POCOs/DTOs like:



public class PriceDTO

public decimal NetAmount get; set;
public decimal GrossAmount get; set;



And I have to check there as well which one was passed to decide from which to calculate.










share|improve this question















I have a class that contains 2 properties of the same type. decimal NetAmount and decimal GrossAmount



I would like to initialize it using either GrossAmount or NetAmount and based on the one specified calculate the second one.



Which way is the most elegant and why? (parameter validation is omitted for brevity)



1



public class TaxedPrice

private TaxedPrice()


public decimal NetAmount get; private set;
public decimal GrossAmount get; private set;

public static TaxedPrice FromNet(decimal netAmount, decimal taxRate)

return new TaxedPrice

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero),
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero)
;


public static TaxedPrice FromGross(decimal grossAmount, decimal taxRate)

return new TaxedPrice

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero),
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero)
;




2



public class TaxedPrice

public TaxedPrice(decimal netAmount, decimal grossAmount, decimal taxRate)

if (grossAmount != default)

GrossAmount = decimal.Round(grossAmount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(grossAmount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (netAmount != default)

NetAmount = decimal.Round(netAmount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(netAmount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

throw new InvalidOperationException($"Either nameof(netAmount) or grossAmount must be set.");



public decimal NetAmount get;
public decimal GrossAmount get;



3



public class TaxedPrice

public enum Type

Gross,
Net


public TaxedPrice(decimal amount, Type type, decimal taxRate)

if (type == Type.Gross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else if (type == Type.Net)

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



4



public class TaxedPrice

public TaxedPrice(decimal amount, bool fromGross, decimal taxRate)

if (fromGross)

GrossAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
NetAmount = decimal.Round(amount.RemoveTax(taxRate), 2, MidpointRounding.AwayFromZero);

else

NetAmount = decimal.Round(amount, 2, MidpointRounding.AwayFromZero);
GrossAmount = decimal.Round(amount.ApplyTax(taxRate), 2, MidpointRounding.AwayFromZero);



public decimal NetAmount get;
public decimal GrossAmount get;



How it looks like from caller's side:



// 1
var taxedPrice = TaxedPrice.FromNet(2.123m, 0.23m);
// 2
var taxedPrice = new TaxedPrice(2.123m, default, 0.23m); // uses the first one to calculate the second one
var taxedPrice2 = new TaxedPrice(2.123m, 1.11m, 0.23m); // uses the first one to calculate the second one
var taxedPrice3 = new TaxedPrice(default, 1.11m, 0.23m); // uses the second one to calculate the first one
// 3
var taxedPrice = new TaxedPrice(2.123m, TaxedPrice.Type.Net, 0.23m);
// 4
var taxedPrice = new TaxedPrice(2.123m, false, 0.23m);


Extensions for tax:



public static class TaxExtensions

public static decimal ApplyTax(this decimal netPrice, decimal taxRate)

return netPrice * (taxRate + 1);


public static decimal RemoveTax(this decimal grossPrice, decimal taxRate)

return grossPrice / (taxRate + 1);




In my upper layer I pass those prices in POCOs/DTOs like:



public class PriceDTO

public decimal NetAmount get; set;
public decimal GrossAmount get; set;



And I have to check there as well which one was passed to decide from which to calculate.







c# comparative-review finance






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 30 mins ago

























asked 1 hour ago









Konrad

1236




1236











  • There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
    – t3chb0t
    1 hour ago










  • @t3chb0t simple extensions for decimal type that add/remove tax from the price.
    – Konrad
    1 hour ago











  • ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
    – t3chb0t
    59 mins ago






  • 1




    @t3chb0t added code
    – Konrad
    56 mins ago










  • oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
    – t3chb0t
    52 mins ago
















  • There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
    – t3chb0t
    1 hour ago










  • @t3chb0t simple extensions for decimal type that add/remove tax from the price.
    – Konrad
    1 hour ago











  • ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
    – t3chb0t
    59 mins ago






  • 1




    @t3chb0t added code
    – Konrad
    56 mins ago










  • oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
    – t3chb0t
    52 mins ago















There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
– t3chb0t
1 hour ago




There are some methods missing: RemoveTax and ApplyTax. Are these methods extensions?
– t3chb0t
1 hour ago












@t3chb0t simple extensions for decimal type that add/remove tax from the price.
– Konrad
1 hour ago





@t3chb0t simple extensions for decimal type that add/remove tax from the price.
– Konrad
1 hour ago













ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
– t3chb0t
59 mins ago




ok, it'd be great if you could add them too so that people can copy/paste your code into an IDE and test it if they like ;-)
– t3chb0t
59 mins ago




1




1




@t3chb0t added code
– Konrad
56 mins ago




@t3chb0t added code
– Konrad
56 mins ago












oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
– t3chb0t
52 mins ago




oh, there is one more thing... since you have posted four versions of the TaxedPrice class, it would be very helpful for reviewers if you summarized what are the differences between each version.
– t3chb0t
52 mins ago










1 Answer
1






active

oldest

votes

















up vote
2
down vote













In my opinion, you should go for number 1 (with one small change).



Reasons for choosing this one:



  1. Easy to modify - you can add new FromX method

  2. Clear - it states exactly what do you calculate

  3. Doesn't require explanation beforehand to do some checking by consumer (as in number 2)

Drawback:



(big one) It might disrupt testing, mocking a static method is a nightmare (unless you can afford moles).



Taking this all into account, I'd do a little bit of overengeneering and create a factory(-ish) class to build you a TaxedPrice object. For example:



public class TaxedPriceFactory: ITaxedPriceFactory 
public TaxedPrice CreateFromNet(decimal netAmount, decimal taxRate)
//body


public TaxedPrice CreateFromGross(decimal grossAmount, decimal taxRate)
// body




This should have all three of advantages I mentioned earlier and it is easy to use in tests now.



Addendum:
Consider making your TaxedPrice a immutable struct instead of a class with two read-only properties.



EDIT: class and method names changed as suggested by @t3chb0t






share|improve this answer


















  • 1




    I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
    – t3chb0t
    19 mins ago











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',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204832%2felegant-way-to-initialize-from-one-of-two-values-of-the-same-type%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote













In my opinion, you should go for number 1 (with one small change).



Reasons for choosing this one:



  1. Easy to modify - you can add new FromX method

  2. Clear - it states exactly what do you calculate

  3. Doesn't require explanation beforehand to do some checking by consumer (as in number 2)

Drawback:



(big one) It might disrupt testing, mocking a static method is a nightmare (unless you can afford moles).



Taking this all into account, I'd do a little bit of overengeneering and create a factory(-ish) class to build you a TaxedPrice object. For example:



public class TaxedPriceFactory: ITaxedPriceFactory 
public TaxedPrice CreateFromNet(decimal netAmount, decimal taxRate)
//body


public TaxedPrice CreateFromGross(decimal grossAmount, decimal taxRate)
// body




This should have all three of advantages I mentioned earlier and it is easy to use in tests now.



Addendum:
Consider making your TaxedPrice a immutable struct instead of a class with two read-only properties.



EDIT: class and method names changed as suggested by @t3chb0t






share|improve this answer


















  • 1




    I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
    – t3chb0t
    19 mins ago















up vote
2
down vote













In my opinion, you should go for number 1 (with one small change).



Reasons for choosing this one:



  1. Easy to modify - you can add new FromX method

  2. Clear - it states exactly what do you calculate

  3. Doesn't require explanation beforehand to do some checking by consumer (as in number 2)

Drawback:



(big one) It might disrupt testing, mocking a static method is a nightmare (unless you can afford moles).



Taking this all into account, I'd do a little bit of overengeneering and create a factory(-ish) class to build you a TaxedPrice object. For example:



public class TaxedPriceFactory: ITaxedPriceFactory 
public TaxedPrice CreateFromNet(decimal netAmount, decimal taxRate)
//body


public TaxedPrice CreateFromGross(decimal grossAmount, decimal taxRate)
// body




This should have all three of advantages I mentioned earlier and it is easy to use in tests now.



Addendum:
Consider making your TaxedPrice a immutable struct instead of a class with two read-only properties.



EDIT: class and method names changed as suggested by @t3chb0t






share|improve this answer


















  • 1




    I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
    – t3chb0t
    19 mins ago













up vote
2
down vote










up vote
2
down vote









In my opinion, you should go for number 1 (with one small change).



Reasons for choosing this one:



  1. Easy to modify - you can add new FromX method

  2. Clear - it states exactly what do you calculate

  3. Doesn't require explanation beforehand to do some checking by consumer (as in number 2)

Drawback:



(big one) It might disrupt testing, mocking a static method is a nightmare (unless you can afford moles).



Taking this all into account, I'd do a little bit of overengeneering and create a factory(-ish) class to build you a TaxedPrice object. For example:



public class TaxedPriceFactory: ITaxedPriceFactory 
public TaxedPrice CreateFromNet(decimal netAmount, decimal taxRate)
//body


public TaxedPrice CreateFromGross(decimal grossAmount, decimal taxRate)
// body




This should have all three of advantages I mentioned earlier and it is easy to use in tests now.



Addendum:
Consider making your TaxedPrice a immutable struct instead of a class with two read-only properties.



EDIT: class and method names changed as suggested by @t3chb0t






share|improve this answer














In my opinion, you should go for number 1 (with one small change).



Reasons for choosing this one:



  1. Easy to modify - you can add new FromX method

  2. Clear - it states exactly what do you calculate

  3. Doesn't require explanation beforehand to do some checking by consumer (as in number 2)

Drawback:



(big one) It might disrupt testing, mocking a static method is a nightmare (unless you can afford moles).



Taking this all into account, I'd do a little bit of overengeneering and create a factory(-ish) class to build you a TaxedPrice object. For example:



public class TaxedPriceFactory: ITaxedPriceFactory 
public TaxedPrice CreateFromNet(decimal netAmount, decimal taxRate)
//body


public TaxedPrice CreateFromGross(decimal grossAmount, decimal taxRate)
// body




This should have all three of advantages I mentioned earlier and it is easy to use in tests now.



Addendum:
Consider making your TaxedPrice a immutable struct instead of a class with two read-only properties.



EDIT: class and method names changed as suggested by @t3chb0t







share|improve this answer














share|improve this answer



share|improve this answer








edited 17 mins ago

























answered 31 mins ago









MaLiN2223

57719




57719







  • 1




    I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
    – t3chb0t
    19 mins ago













  • 1




    I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
    – t3chb0t
    19 mins ago








1




1




I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
– t3chb0t
19 mins ago





I don't think this is overengineered. If you named it TaxedPriceFactory and CreateFromNet it'd be exactly as books suggest it ;-)
– t3chb0t
19 mins ago


















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204832%2felegant-way-to-initialize-from-one-of-two-values-of-the-same-type%23new-answer', 'question_page');

);

Post as a guest













































































Comments

Popular posts from this blog

What does second last employer means? [closed]

List of Gilmore Girls characters

Confectionery