One of the fundamentals of Object Orientation is no exposure of the implementation details of an object. I’ve talked a few times this in another articles.
So we use the encapsulation of the implementation, so do not put our objects and we have a better maintainability of our classes and since this encapsulation ensures the integrity of our objects can also ensure that the user will only be able to access our objects defined through a contract with a public interface.
But often we begin to implement these rules defining our attributes as private and only allowing access to these through getters and setters:
<cfcomponent displayname="ShopCart"> <cfscript> public ShopCart function init() { this.products = []; this.totalAmount = 0; } public Void function setProducts( Product val ) { ArrayAppend( this.products , arguments.val ); } public Array function getProducts() { return this.products; } public Void function setTotalAmount( Numeric val ) { this.totalAmount = arguments.val; } public Numeric function getTotalAmount() { return this.totalAmount; } </cfscript> </cfcomponent>
First let’s understand that I’m not speaking of a Data Transfer Object (DTO) or BEAN or VO, BO …. I’m talking about a simple class. It could be a business class, accessed by a controller interface to record data ShopCart to some virtual store.
Understood it, we can analyze the above class, have some methods that go against the concept of encapsulation ofObject Orientation.
When we use getters and setters have to be careful because not always the setter is the most correct. Let’s see why.
In this particular case, we have not totalAmount attribute value must be “replaced” but added or subtracted according to the operation performed in the user interface.
In reality totalAmount involves much more than we think, our classes should be intelligent and must have a business rule, remember the concept of Responsibility Division ShopCart class has the responsibility to determine the total purchase, with your taxes, freight and everything else.
If the object ShopCart depend on someone to determine these values, you dummy and in the event of a change in the interface or other objects can cause interference with its proper operation.
And then, what are our options?
The first principle is the concept Tell, Do not Ask, meaning you must tell the objects what to do and do not ask, for example: calculateTotalAmount() and avoid being the object as always asking getTotalAmount().
We can then implement Dependency Injection(DI) for a class of logic that will do the calculation separately ShopCart class eg a class “Shopping”
<cfcomponent displayname="Shopping"> <cfscript> variables.ADD_PRODUCT = 1; variables.REMOVE_PRODUCT = 0; public Void function calculateTotalAmount( ShopCart shopCart , Numeric productValue , Numeric tax , Numeric actionType ) { arguments.shopCart.setTotalAmount( if( arguments.actionType == variables.ADD_PRODUCT ) { arguments.shopCart.getTotalAmount() + arguments.productValue + arguments.tax ); } else if( arguments.actionType == variables.REMOVE_PRODUCT ) { arguments.shopCart.getTotalAmount() - arguments.productValue - arguments.tax ); } else { throw("Objeto","Invalid Operation"); abort; } ); } </cfscript> </cfcomponent>
This kind of class is very structured, very clear message travels between objects, and in case of maintenance or change of business rules is very easy to be modified.
But what we did with the class ShopCart is called Model Domain Anemic, we took all the responsibilities of our class, in which case simply turned into a library of functions that do what someone asks.
It can work, and even be advantageous, we should only take care not to transform our class in libraries… One option that would unite the logical with the class ShopCart, inserting her responsibilities. Paying attention to this responsibility must be inserted with the scope of the object.
Read abour Division of Responsability
<cfcomponent displayname="ShopCart"> <cfscript> variables.ADD_PRODUCT = 1; variables.REMOVE_PRODUCT = 0; public ShopCart function init() { this.products = []; this.totalAmount = 0; } public Void function setProducts( Products val ) { ArrayAppend( this.products , arguments.val ); } public Array function getProducts() { return this.products; } public Numeric function getTotalAmount() { return this.totalAmount; } public Void function calculateTotalAmount( Numeric productValue , Numeric tax , Numeric actionType ) { if( arguments.actionType == variables.ADD_PRODUCT ) { this.valorTotal = this.getTotalAmount() + arguments.productValue + arguments.tax ); } else if( arguments.actionType == variables.REMOVE_PRODUCT ) { this.valorTotal = this.getTotalAmount() - arguments.productValue - arguments.tax ); } else { throw("Objeto","Invalid Operation"); abort; } } </cfscript> </cfcomponent>
As we have seen, most do not implement the setter of the attribute totalAmount, and kept the getter. We kept him because he is part of the domain. To follow this rule, we have to repeat this kind of implementation for the products as well, since before entering a product, we should see if it still exists in stock, if the customer is making a request for when the product it receives and etc … So we had a class with setters and more functional features.
We can integrate patterns to generate richer class’s and not just silly class’s without any intelligence.
Realize that every item that we studied, we see the involvement of patterns already studied. This demonstrates the importance you see on other patterns. It’s no use you study this pattern without knowing what is Dependency Injection. So if you have not seen the other articles, this is the time to take a read on each pattern.
I hope I have been clear in this article, this time not come to pass a new standard, but attention to concerns we have when we are doing a software architecture.
Thank you.