Refactoring: Improving the Design of Existing Code, Second Edition (Garner McCloud's Library) by Martin Fowler
Author:Martin Fowler
Language: eng
Format: epub
Publisher: Addison-Wesley Professional
Published: 2019-07-15T00:00:00+00:00
Another approach is to return a read-only proxy for the data structure. Such a proxy could raise an exception if the client code tries to modify the underlying object. Some languages make this easy, but it’s a pain in JavaScript, so I’ll leave it as an exercise for the reader. I could also take a copy and recursively freeze it to detect any modifications.
Dealing with the updates is valuable, but what about the readers? Here there are a few options.
The first option is to do the same thing as I did for the setters. Extract all the reads into their own functions and move them into the customer data class.
class CustomerData…
Click here to view code image
usage(customerID, year, month) { return this._data[customerID].usages[year][month]; }
top level…
Click here to view code image
function compareUsage (customerID, laterYear, month) { const later = getCustomerData().usage(customerID, laterYear, month); const earlier = getCustomerData().usage(customerID, laterYear - 1, month); return {laterAmount: later, change: later - earlier}; }
The great thing about this approach is that it gives customerData an explicit API that captures all the uses made of it. I can look at the class and see all their uses of the data. But this can be a lot of code for lots of special cases. Modern languages provide good affordances for digging into a list-and-hash [mf-lh] data structure, so it’s useful to give clients just such a data structure to work with.
If the client wants a data structure, I can just hand out the actual data. But the problem with this is that there’s no way to prevent clients from modifying the data directly, which breaks the whole point of encapsulating all the updates inside functions. Consequently, the simplest thing to do is to provide a copy of the underlying data, using the rawData method I wrote earlier.
class CustomerData…
Click here to view code image
get rawData() { return _.cloneDeep(this._data); }
top level…
Click here to view code image
function compareUsage (customerID, laterYear, month) { const later = getCustomerData().rawData[customerID].usages[laterYear][month]; const earlier = getCustomerData().rawData[customerID].usages[laterYear - 1][month]; return {laterAmount: later, change: later - earlier}; }
But although it’s simple, there are downsides. The most obvious problem is the cost of copying a large data structure, which may turn out to be a performance problem. As with anything like this, however, the performance cost might be acceptable—I would want to measure its impact before I start to worry about it. There may also be confusion if clients expect modifying the copied data to modify the original. In those cases, a read-only proxy or freezing the copied data might provide a helpful error should they do this.
Another option is more work, but offers the most control: Apply Encapsulate Record recursively. With this, I turn the customer record into its own class, apply Encapsulate Collection (170) to the usages, and create a usage class. I can then enforce control of updates by using accessors, perhaps applying Change Reference to Value (252) on the usage objects. But this can be a lot of effort for a large data structure—and not really needed if I don’t access that much of the data structure.
Download
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.
Deep Learning with Python by François Chollet(12566)
Hello! Python by Anthony Briggs(9911)
OCA Java SE 8 Programmer I Certification Guide by Mala Gupta(9795)
The Mikado Method by Ola Ellnestam Daniel Brolund(9777)
Dependency Injection in .NET by Mark Seemann(9336)
Algorithms of the Intelligent Web by Haralambos Marmanis;Dmitry Babenko(8293)
Test-Driven iOS Development with Swift 4 by Dominik Hauser(7758)
Grails in Action by Glen Smith Peter Ledbrook(7693)
The Well-Grounded Java Developer by Benjamin J. Evans Martijn Verburg(7557)
Becoming a Dynamics 365 Finance and Supply Chain Solution Architect by Brent Dawson(7034)
Microservices with Go by Alexander Shuiskov(6799)
Practical Design Patterns for Java Developers by Miroslav Wengner(6712)
Test Automation Engineering Handbook by Manikandan Sambamurthy(6652)
Secrets of the JavaScript Ninja by John Resig Bear Bibeault(6409)
Angular Projects - Third Edition by Aristeidis Bampakos(6060)
The Art of Crafting User Stories by The Art of Crafting User Stories(5591)
NetSuite for Consultants - Second Edition by Peter Ries(5526)
Demystifying Cryptography with OpenSSL 3.0 by Alexei Khlebnikov(5330)
Kotlin in Action by Dmitry Jemerov(5062)
