A very long time in the past in a weblog put up far, far-off… Andy wrote about Han Solo Encapsulation – to maintain Jabba’s “system working as designed he wanted to encapsulate his system behind a rock strong interface”.
By a stroke of fine fortune, or even handed design selections relying in your perspective, a current refactoring job that I assumed may have far reaching penalties changed into just a few modifications in a single class, in the end saving a lot time. Let’s take a look.
My situation is that I’ve a DataProvider that accepts a DataRequest and returns some information:
classdef DataProvider strategies perform information = getData(dataProvider,dataRequest) arguments dataProvider (1,1) DataProvider dataRequest (1,1) DataRequest finish finish finish finish
The DataRequest appears like this:
classdef DataRequest properties Make (1,1) string = lacking Mannequin (1,1) string = lacking ManufacturingYear (1,1) datetime = lacking finish finish
It defines related properties that the DataProvider must serve up the information. All of the values are scalar and have default values of lacking to signify the truth that they’re “unset” by the person.
To kind its question, the DataProvider should extract the values of every property. To do that, it must know that “unset” is represented by “lacking” and due to this fact it needs to be ignored from the search situation.
Right here’s one doable implementation in DataProvider:
classdef DataProvider strategies perform information = getData(dataProvider,dataRequest) arguments dataProvider (1,1) DataProvider dataRequest (1,1) DataRequest finish searchTerms = {}; propsToGet = ["Make" "ManufacturingYear" "Model"]; for prop = propsToGet if ~ismissing(dataRequest.(prop)) searchTerms = [searchTerms {prop} {dataRequest.(prop)}]; finish finish outcome = dataProvider.search(searchTerms{:}); finish finish finish
The issue right here is that DataProvider, and every other code that makes use of DataRequest, is coupled to how DataRequest represents its “unset” values. That’s one thing that needs to be encapsulated inside the DataRequest. What my DataProvider actually desires is an array of name-value pairs of non-missing property names and values.
(We may additionally describe this for example of the inform don’t ask precept since we’re not truly hiding DataRequest’s properties from the skin world.)
Let’s refactor the DataRequest so as to add a technique that does simply that. I’ve referred to as it namedargs2cell as a result of its similarity to the built-in MATLAB perform.
classdef DataRequest properties Make (1,1) string = lacking Mannequin (1,1) string = lacking ManufacturingYear (1,1) datetime = lacking finish strategies perform paramCell = namedargs2cell(dataRequest) arguments dataRequest (1,1) DataRequest finish paramCell = {}; propsToGet = ["Make" "ManufacturingYear" "Model"]; for prop = propsToGet if ~ismissing(dataRequest.(prop)) paramCell = [paramCell {prop} {dataRequest.(prop)}]; finish finish finish finish finish
This makes our DataProvider a lot easier:
classdef DataProvider strategies perform information = getData(dataProvider,request) arguments dataProvider (1,1) DataProvider request (1,1) DataRequest finish searchTerms = namedargs2cell(request); outcome = dataProvider.search(searchTerms{:}); finish finish finish
Returning to our encapsulation precept, what can we obtain? In my real-life case, I wished to vary the DataRequest to permit a number of values to be specified for a given property somewhat than simply scalars. It due to this fact is sensible to signify “unset” with an empty somewhat that with a scalar lacking. Since all information of how “unset” is represented is contained inside the DataRequest, the solely modifications I wanted to make had been inside DataRequest itself. No different code needed to be touched:
classdef DataRequest properties Make (1,:) string Mannequin (1,:) string ManufacturingYear (1,:) datetime finish strategies perform paramCell = namedargs2cell(dataRequest) arguments dataRequest (1,1) DataRequest finish paramCell = {}; propsToGet = ["Make" "ManufacturingYear" "Model"]; for prop = propsToGet if ~all(isempty(dataRequest.(prop))) paramCell = [paramCell {prop} {dataRequest.(prop)}]; finish finish finish finish finish
Within the above, observe how ismissing has change into all(isempty(…)).
In conclusion, by offering the proper interface to your courses, code modifications can change into rather more restricted in scope and simpler to implement.
Printed with MATLAB® R2022b