Back
on 2010 I worked on two projects for couple of months. For one project I used
Eclipse/Java and for the other I used VS 2008/C#.
I remembered the day that one of our Java colleagues commented on Automatic
properties, back then it was a new feature of C# (C# 3.0)
He said: "The way that you can implement properties in C# you might as
well declared them public".
He was referring to this:
public decimal ExchangeRate {get;set;}; //automatic property
being the equivalent of this:
public decimal ExchangeRate. //public variable
I understand, that for automatic properties, the compiler would automatically create the required private fields. Still that property can be assigned a value from any part of your code.
The issue that I see, and other people do, with automatic properties is that a class with automatic properties doesn't protect its invariants. "A class invariant expresses properties of an object which are stable in between operations initiated via the public client interface".
I want to illustrate this with a small example
public decimal ExchangeRate. //public variable
I understand, that for automatic properties, the compiler would automatically create the required private fields. Still that property can be assigned a value from any part of your code.
The issue that I see, and other people do, with automatic properties is that a class with automatic properties doesn't protect its invariants. "A class invariant expresses properties of an object which are stable in between operations initiated via the public client interface".
I want to illustrate this with a small example
public class CurrencyInfo
{
public int CurrencyId { get; set; }
public string CurrencyCode { get; set; }
public decimal ExchangeRate { get; set; }
public string Name { get; set; }
}
{
public int CurrencyId { get; set; }
public string CurrencyCode { get; set; }
public decimal ExchangeRate { get; set; }
public string Name { get; set; }
}
We were tasked on creating a functionality that allows our software to perform a Currency conversion between other currencies and the Australian dollars, also known on the Australian market as the Little Battler. The CurrencyInfo class, above, is one of the classes that makes the Currency Conversion API.
Let's say that we also have a service that Update the currency using the class CurrencyInfo.
public bool UpdateCurrency(CurrencyInfo currencyInfo, string userName)
{
_logger.log(currencyInfo.ExchangeRate ,userName, currencyInfo.CurrencyCode
..............
...............//code that does the updating goes here
...............//and here
{
_logger.log(currencyInfo.ExchangeRate ,userName, currencyInfo.CurrencyCode
..............
...............//code that does the updating goes here
...............//and here
}
What would happen if a developer, using our API, forget to set the CurrencyCode property?
We might think that We are safe because some validation can be applied on the UI level so not null or empty values would make their way into the CurrencyInfo class.
However, we write code for reuse and it is not uncommon that many API get used by Cron jobs or Windows services. Think about a poller, polling salaries in Singaporean currency that needs to be converted to Australian currency. Can we guarantee that a property such as CurrencyCode is set to a correct value?
I like the idea of checking for properties values in the class where they are declared. Another approach would be to do this kind of check on the Application Service Layer. I think that with this approach we would need to check that the CurrencyInfo class has valid values on every Service class or Servcie's method that uses it.
By having check in our properties the class invariant are protected and the class or instance of the class don't go into an invalid state.
Remember that what is a 'valid' value varies depending on the business requirements. In one application a Salary of 0 might be a valid value whereas on another might not.
public int CurrencyId
{
get
{
return currencyId;
}
set
{
//we don't have currency id negative or zero
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
currencyId = value;
}
}
public string CurrencyCode
{
get
{
return currencyCode;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
currencyCode = value;
}
}
public decimal ExchangeRate
{
get
{
return exchangeRate;
}
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
exchangeRate = value;
}
}
{
get
{
return currencyId;
}
set
{
//we don't have currency id negative or zero
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
currencyId = value;
}
}
public string CurrencyCode
{
get
{
return currencyCode;
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
currencyCode = value;
}
}
public decimal ExchangeRate
{
get
{
return exchangeRate;
}
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
exchangeRate = value;
}
}
So
now with the safe guard checks in place, We could go one step further and make
sure that users of the CurrencyInfo are obligated to provide the property
values when they create an instance of the CurrencyInfo.
We could do this by making the setters private and creating a constructor that take the properties' values.
We could do this by making the setters private and creating a constructor that take the properties' values.
public CurrencyInfo(int currencyId, string currencyCode,
decimal exchangeRate, string name)
{
CurrencyId = currencyId;
CurrencyCode = currencyCode;
ExchangeRate = exchangeRate;
Name = name;
}
public int CurrencyId
{
get
{
return _currencyId;
}
private set
{
//we don't have currency id negative or zero
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
_currencyId = value;
}
}
public string CurrencyCode
{
get
{
return _currencyCode;
}
private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_currencyCode = value;
}
}
public decimal ExchangeRate
{
get
{
return _exchangeRate;
}
private set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
_exchangeRate = value;
}
}
public string Name
{
get
{
return _name;
}
private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_name = value;
}
}
}
decimal exchangeRate, string name)
{
CurrencyId = currencyId;
CurrencyCode = currencyCode;
ExchangeRate = exchangeRate;
Name = name;
}
public int CurrencyId
{
get
{
return _currencyId;
}
private set
{
//we don't have currency id negative or zero
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
_currencyId = value;
}
}
public string CurrencyCode
{
get
{
return _currencyCode;
}
private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_currencyCode = value;
}
}
public decimal ExchangeRate
{
get
{
return _exchangeRate;
}
private set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
_exchangeRate = value;
}
}
public string Name
{
get
{
return _name;
}
private set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_name = value;
}
}
}
Now the users/developers of this class must supplied the properties values when they created an instance of it . Otherwise, it won't compile. If they make a mistake and pass invalid parameters in the constructor then the class has safe guard checks which will provide a feedback , in the form of exception, before the class get used by other part of code.
A while ago I used to think that this approach tends to create a lot of code. However, this code gets re-used every time that someone uses the CurrencyInfo. This approach takes the load off the the application's Services as they have a guarantee that any CurrencyInfo instance has the appropriate values.
The application's Services don't have to check if a flag or a property is not null; or if a value stored in an instance of the CurrencyInfo is valid within the context of the business requirements.
The Application's Services are left to do what they do best, the orchestration of other classes and repositories, to achieve a given outcome.
Perhaps
there is a tool out there that can help with the protecting of class invariants
using a Aspect-oriented programming
approach.
The key point is that automatic properties don't protect the class invariants. I can see the usage of automatic properties in DTO's as the DTO properties get populated with values before they are serialize and because our CurrencyInfo is guarantee to have valid values these values could be mapped to a DTO.
Technical References:
The key point is that automatic properties don't protect the class invariants. I can see the usage of automatic properties in DTO's as the DTO properties get populated with values before they are serialize and because our CurrencyInfo is guarantee to have valid values these values could be mapped to a DTO.
Technical References:
http://people.cs.aau.dk/~normark/oop-csharp/html/notes/contracts_themes-class-inv-sect.html
http://blog.ploeh.dk/2011/05/26/CodeSmellAutomaticProperty/
https://pragprog.com/articles/tell-dont-ask
http://blog.ploeh.dk/2011/05/26/CodeSmellAutomaticProperty/
https://pragprog.com/articles/tell-dont-ask