I'm working through some stuff where I have a registry of services. Each service is put into the registry using a Put() method and stored as an object. I'd like that only the registry be able to modify the service objects. I have a GetService() method which returns the registered instance. I'd like that returned instance to be immutable (ie not change the values in my registry). I can (I suppose) return a clone of the registered object, but that becomes painful when the service has (a) collection(s) of child objects, which themselves may have children. Now I have to iterate all the way down, when I want to return a single instance. This seems ... painful.
I suppose I could be "smart" and use temp-tables to back the object properties (so just have the properties etc be a strongly-typed facade on temp-table fields) but this has it's own problems. Making individual objects immutable is easy enough ig you make all properties read-only and pass an entire object's definition via the constructor. I'm looking for "sometimes mutable" objects.
Just wondering what your (collective) thoughts on immutable objects (and how to implement in ABL cheaply/simply) in general, and on this topic are.
My thoughts:
If the service's behavior doesn't change after a prior call (ie does not persist state) then the implementation is already immutable.
If the service persists some state after it's called and that state could/will affect the behavior of follow-on calls, then you need to make a copy of the service and return the copy -or- separate out the functionality which persists the service's state and give the calling code a copy of that while retaining the state-free part of the code as the core of the service.
I'm wondering a bit why you are focused on immutability as a concept. It seems to me that a particular type of object has certain behavior. If that behavior includes only read-only requirements, then it is provided only with read only methods, although that implies setting through the constructor, which I am not nuts about unless it is very simple. If it needs both read and write methods, but it should only be writeable up to some point where it becomes read only, like a Client Principal, the provide a switch which disables the write methods once the switch is thrown. If that is true for outside users, but not for the creator, then provide a key protected method for flipping the switch back. I.e., the object needs to have whatever behavior it needs to have and you need to provide methods which implement that behavior and not provide any other methods which do something not in the behavior set.
If the objects are supposed to be immutable, how does the registry modify the service objects?
How do you determine if a call to a Put() or Remove() is valid?
what I think you are describing is reactive programming (see github.com/.../platform for an angular 4 version)
Core tenets:
State is a single immutable data structure
Actions describe state changes
Pure functions called reducers take the previous state and the next action to compute the new state
State accessed with the Store, an observable of state and an observer of actions
Basically, coding to events. A component *does not* mutate an object : it sends the existing object to a reducer which creates a new object, and mutates that object according to the reducer function. A new state is then broadcast , and any interested subscriber gets the new state.
The idea of the store is to have one and only one single place of truth, which means all the objects are immutable, and the only way to change anything is to recreate everything as a whole.
For a bit of background on reactive programming read "out of the tarpit": curtclifton.net/.../MoseleyMarks06a.pdf
(named in f.e. onehungrymind.com/.../fem-reactive.pdf "Learn to Build Reactive Apps with Angular" also). Out of the tarpit is one of my favourite documents about software development.
I assume that the intent of a caller making Put() and Remove() calls into the registry is to modify the registry. Assuming valid data is passed in, anyone can modify the contents.
I cannot assume that when a caller has a reference to a service def object and changes properties on that instance, that they intend to change the canonical values..
I think the right answer may be to return a new instance every time . But I'm concerned about performance of cloning (especially if there are deep/large object graphs)
Maybe your question about changing immutable objects still isn't clear, even when assuming valid data is passed in? :-)
Just playing devil's advocate;
"I cannot assume that when a caller has a reference to a service def object and changes properties on that instance, that they intend to change the canonical values."
I don't think you can also assume the opposite. Some might expect it to change when modified.
so that's the whole point of the reactive coding. You dispatch the original object with the action and data and the reducer creates a new object based on the original, action and data. This is then stored and the new value emitted
So there is a continuous stream of events and new versions of the object being monitored. This also allows you to build a "time machine" (a hook can take this stream of new versions and stack them) which allows you to easily add undo/redo capabilities to the system
the important take away is that there is only one place of code that can "modify" (create a new version) of the object, and one "source of truth"
I don't think you can also assume the opposite.
I don't know that one can assume one or the other ... it should be a part of the rules for the object type.
Maybe some helpful info can be found in redux docs f.e. redux.js.org/.../Reducers.html