Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
The first recurring question had to do with the notions of Employees and their salary attribute: How can you sum all the salaries of all the employees in some department without violating encapsulation?
Or, to put it another way, why don't you have to get a number out of the Employee class to perform this operation?
My answer is that you don't get the value of the salary from the Salary object. You might have a salary() method that returns a Salary object that represents the salary, but the implementation of the salary is still hidden inside an object. The main purpose
of the auxiliary Salary object is to remove the salary-manipulation functions from the main Employee interface and put them into a class of their own. That is, rather than give the Employee a print_your_salary() method, you'd give the Salary a print_yourself() method. (The salary would also support methods to do other salary-related operations, of course.) You could then print an employee's salary with
either:
some_employee.print_your_salary();
or
some_employee.salary().print_yourself();
Adding its salary to a sum is also a reasonable request to ask of Employee in some situations; both of the following are workable object-oriented solutions, since neither exposes the way in which
salaries are implemented:
some_employee.add_your_salary_to(
Salary the_sum )
{ the_sum.add( my_salary );
}
Or, if you had resorted to a get method:
the_sum.add( some_employee.salary() );
The second issue readers brought to my attention was: How do you handle interconnected controls on a form? For example, what if the status of some checkbox determines which values are valid in another proxy entirely? The quick answer is that the interaction happens at the model (abstraction) level, and the proxies take care of themselves.
The figure below demonstrates two common situations. On the left, a model-level object has displayed proxies for two attributes, one as a checkbox and the other as a field of some sort. When the user clicks on the checkbox, a model-level listener (manufactured when the proxies are created) notices that the checkbox has changed state and enables the associated field.
The more interesting example is on the right side of the figure. Here, there is a complex interaction between proxies for
three objects of different classes: an instance of Company called employer, an instance of Group called division, and an instance of Person called employee. The proxy for the Company displays a list of divisions. It can do this without any communication with the rest of the system because of the qualified
association. (Think of the qualifier as specifying a hashtable of Group objects, keyed by division Name. The Company can create a UI displaying all the division names simply by calling getKeys() on the hashtable.)
Now look at the sequence diagram shown in the comment on the figure. When the user selects a division's name, the model-level
employer object looks up that division in the hashtable, then asks the division to display its employees. The division turns around and iterates across its list of employees, asking each one for a proxy, which division displays on some UI (a list, for example) that it's creating. When it's done with the traversal, it displays the list. The
UI seems to be unified in that selecting a division causes a list of employees to change, but it's really model-level communication
that's going on, not communication between proxies. Of course, the Bag wrapper I discussed last month would simplify the implementation considerably, since it would take care of creating the lists.

Intercommunication between proxies
Tabbing order and focus shift would be handled in much the same way. Either the model-level entity that creates the proxy
would establish these behaviors when it created the proxies, or the associated control object would do so. For example, in
the Form class that we looked at two months ago, the Form object could easily establish a tab ordering that would follow the order in which Element objects appeared in the form description. Invariant fields would not be put into the tab ordering at all.
The final question that kept coming up in your letters was also one of interaction: What about data validation upon close, rather than on the fly? What about a cancel button undoing any changes requested by the user after the form displayed?
This one is more complicated. In order to make the architecture more clear, my earlier article presented a simplified version
of the system that I actually use, and though I don't want to rewrite the article, I can at least explain the more sophisticated
architecture. Rather than returning a simple JComponent, you could rewrite the User_interface to support transaction-based messaging, as follows:
interface Transaction { public void begin(); public void commit(); public void rollback(); public int supported_rollback_levels(); } abstract class Visual_proxy extends JComponent implements Transaction {} interface User_interface { public Visual_proxy proxy( String attribute_name, boolean is_read_only ); }
The Transaction interface supports begin() and commit() operations. (The former effectively activates the proxy; the user can then interact with it, but the results of that interaction
are not sent to the abstraction-level object until a commit() is issued.) The rollback() method causes the proxy to interact with the model, and should put the model back into the state it was in before a previous
commit() was issued. You can use this mechanism for undo operations and the like. The supported_rollback_levels() tells the control object how many levels of rollback are supported. It could return 0 if rollback isn't supported at all.
Given this structure, the control object has choices that it didn't have before, and can easily support the requested features.
For data validation on close, the validation is done in the commit() operation, which throws up a dialog box or equivalent if everything's not OK. Cancel is easily implemented, either by not
committing until an OK button is pressed, or by rolling back all committed transactions when the Cancel button is pressed.