Reputation: 753
Let us say that we have 2 Classes Expense1
and Expense2
. Which class implementation is preferred over the other, or which is considered closer to being object-oriented?
I always thought that doing Exp2.Calculate(1.5M,2)
is more readable than
exp1.Calculate()
and using the properties of exp1 Class as the needed values for the calculate Method.
Expense1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Expense1
{
public decimal ExpenseValue { get; set; }
public int NumberOfItems { get; set; }
private decimal result;
public decimal Result
{
get
{
return this.NumberOfItems * this.ExpenseValue;
}
}
public void Calculate()
{
this.result = this.ExpenseValue * this.NumberOfItems;
}
public void expense1()
{
this.ExpenseValue = 0;
this.NumberOfItems = 0;
}
}
Expense2
class Expense2
{
public decimal Calculate(decimal expenseValue, int numberOfItems)
{
return expenseValue * numberOfItems;
}
}
Implementation of both classes
class Program
{
static void Main(string[] args)
{
Expense1 exp1 = new Expense1();
exp1.NumberOfItems = 2;
exp1.ExpenseValue = 1.5M ;
exp1.Calculate();
Console.WriteLine("Expense1:" + exp1.Result.ToString());
Expense2 exp2 = new Expense2();
string result = string.Empty;
result = exp2.Calculate(1.5M,2).ToString();
Console.WriteLine("Expense2:" + result);
Console.ReadKey();
}
}
Upvotes: 3
Views: 162
Reputation: 2530
Expense2
is easier to read and understand what going on as it's evident from the call which parameters are used.
Expense1
could have various side effects as the caller might forget to set the variables used for calculating before he calls Calculate()
.
If you require access to the values that was used to calculate the result later, you can use something like this
public class Expense {
public decimal ExpenseValue { get; private set; }
public int NumberOfItems { get; private set; }
public decimal Result { get; private set; }
public decimal Calculate(decimal expenseValue, int numberOfItems) {
ExpenseValue = expenseValue;
NumberOfItems = numberOfItems;
Result = ExpenseValue * NumberOfItems;
return Result;
}
public Expense() {
Calculate(0, 0);
}
}
This will allow the internal state of Expense
to stay consistent for the lifetime of the object, with the definition of how it's Calculated
Upvotes: 3
Reputation: 1881
It all depends on what you want to do, but a rule of thumb is use properties when they are really linked to the object, and pass what use need as parameters when they are external to the object.
A simple example, consider having Person as a class, one of its properties is birthday, so in this case you should do this
public class Person{
public DateTime Birthday{get;set;}
public int GetAge(){
//some implementation
}
}
another example, think of having a Cube object, and in this Cube object you have a method Intersect with another cube, in this case you should pass the second cube as parameter and not make it a property of Cube because it is something external.
public class Cube{
public Cube Intersect(Cube other){
//Do your logic here
}
}
Upvotes: 1
Reputation: 3589
I don't like Expense1 one bit. I think properties with public get/set should generally be avoided since it provides no encapsulation. It's hardly even better than public fields (although it does give some room for guard clauses etc down the road).
Worse than that, however, is the Calculate() method imo. It takes no arguments but it still changes the internal state of the object. It is not clear how. At the same time, the field it writes to, result
, isn't being returned by the property Result
, that property does the same calculation again (if you have to do calculations, generally prefer a method over a property).
Finally, it is not necessary to initialize primitive types to their default value in the constructor, number types always get initialized to 0.
===
Expense2 is better, it is more clear what is happening. Method takes two well-named arguments and give a reasonable result straight away.
If this method is the only use-case of the class, however, I would consider renaming it since it does not represent an expense, it represents something close to an expense calculator.
Upvotes: 1
Reputation: 43254
Expense2.Calculate
is deterministic (has the same result every time its called with the same parameters) and because it has no side effects (the parameters are served to it, rather than via properties) it is thread-safe too. Lastly, it is simpler and easier to read.
Expense1
is a classic OOP train-wreck with non-thread safe state, and no guarantee that Result
will return the result of a thread's invocation of Calculate
. Further, it is long-winded and complex to read and maintain.
I'd favour Expense2
over Expense1
every time. The only thing I'd change would be to make it static as there's no gain to having to create an instance of Expense2
to call Calculate
.
Upvotes: 3