Posts Tagged ‘encapsulation’
Book chapter published: Confined Roles and Decapsulation in Object Teams — Contradiction or Synergy?
I strongly believe that for perfect modularity, encapsulation in plain Java is both too weak and too strong. This is the fundamental assumption behind a book chapter that has just been published by Springer.
- The book is:
- Aliasing in Object-Oriented Programming. Types, Analysis and Verification
- My chapter is:
- Confined Roles and Decapsulation in Object Teams — Contradiction or Synergy?
The concepts in this chapter relate back to the academic part of my career, but all of my more pragmatic tasks since those days indicate that we are still very far away from optimal modularity, and both mistakes are found in real world software: to permit access too openly and to restrict access too strictly. More often than not, it’s the same software exhibiting both mistakes at once.
For the reader unfamiliar with the notions of alias control etc., let me borrow from the introduction of the book:
Aliasing, by the way, is one of the reasons, why analysis for @Nullable fields is a thorny issue. If alias control could be applied to @Nullable fields in Java, much better static analysis would be possible.
How is encapsulation in Java too weak?
This manifests at two levels:
Member access across instances
In a previous post I mentioned that the strongest encapsulation in Java – using the private modifier – doesn’t help to protect a person’s wallet against access from any other person. This is a legacy from the pre-object-oriented days of structured programming. In terms of encapsulation, a Java class is a module utterly unaware of the concept of instantiation. This defect is even more frustrating as better precedents (think of Eiffel) have been set before the inception of Java.
A type system that is aware of instances, not just classes, is a prerequisite for any kind of alias control.
Object access meets polymorphism
Assume you declare a class as private because you want to keep a particular thing absolutely local to the current module. Does such a declaration provide sufficient protection? No. That private class may just extend another – public – class or implement a public interface. By using polymorphism (an invisible type widening suffices) an instance of the private class can still be passed around in the entire program – by its public super type. As you can see, applying private at the class level, doesn’t make any objects private, only this particular class is. Since every class extends at least
Object there is no way to confine an object to a given module; by widening all objects are always visible to all parts of the program. Put dynamic binding of method calls into the mix, and all kinds of “interesting” effects can be “achieved” on an object, whose class is “secured” by the private keyword.
The type system of OT/J supports instance-based protection.
Java’s deficiencies outlined above are overcome in OT/J by two major concepts:
- Dependent types
- Any role class is a property of the enclosing team instance. The type system allows reasoning about how a role is owned by this enclosing team instance. (read the spec: 1.2.2)
- Confined roles
- The possible leak by widening can be prevented by sub-classing a predefined role class
Confinedwhich does not extend
Object. (read the spec: 7.2)
For details of the type system, why it is suitable for mending the given problems, and why it doesn’t hurt in day-to-day programming, I have to refer you to the book chapter.
How is encapsulation in Java too strict?
If you are a developer with a protective attitude towards “your” code, you will make a lot of things private. Good for you, you’ve created (relatively) well encapsulated software.
But when someone else is trying to make use of “your” code (re-use) in a slightly unanticipated setting (calling for unanticipated adaptations), guess what: s/he’ll curse you for your protective attitude.
Have you ever tried to re-use an existing class and failed, because some **** detail was private and there was simply no way to access or override that particular piece? When you’ve been in this situation before, you’ll know there are basically 2 answers:
- Give up, simply don’t use that (overly?) protective class and recode the thing (which more often than not causes a ripple effect: want to copy one method, end up copying 5 or more classes). Object-orientation is strong on re-use, heh?
- Use brute force and don’t tell anybody (tools that come in handy are: binary editing the class file, or calling Method.setAccessible(true)). I’m not quite sure why I keep thinking of Core Wars as I write this 🙂 .
OT/J opens doors for negotiation, rather than arms race & battle
The Object Teams solution rests on two pillars:
- Give a name to the act of disregarding an encapsulation boundary: decapsulation. Provide the technical means to punch tiny little holes into the armor of a class / an object. Make this explicit so you can talk and reason about the extent of decapsulation. (read the spec: 2.1.2(c))
- Separate technical possibility from questions of what is / should / shouln’t be allowed. Don’t let technology dictate rules but make it possible to formulate and enforce such rules on top of the existing technology.
Knowing that this topic is controversial I leave at this for now (a previous discussion in this field can be found here).
Putting it all together
- If you want to protect your objects, do so using concepts that are stronger than Java’s “private”.
- Using decapsulation where needed fosters effective re-use without the need for “speculative API”, i.e., making things public “just in case”.
- Dependent types and confined roles are a pragmatic entry into the realm of programs that can be statically analysed, and strong guarantees be given by such analysis.
- Don’t let technology dictate the rules, we need to put the relevant stakeholders back into focus: Module providers, application developers and end users have different views. Technology should just empower each of them to get their job done, and facilitate transparency and analyzability where desired.
Some of this may be surprising, some may sound like a purely academic exercise, but I’m convinced that the above ingredients as supported in OT/J support the development of complex modular systems, with an unprecedented low effective coupling.
FYI, here’re the section headings of my chapter:
- Many Faces of Modularity
- Confined Roles
- Safe Polymorphism
- From Confined Types to Confined Roles
- Adding Basic Flexibility to Confined Roles
- Non-hierarchical Structures
- Role Playing
- Translation Polymorphism
- Separate Worlds, Yet Connected
- Layered Designs
- Improving Encapsulation by Means of Decapsulation
- Zero Reference Roles
- Connecting Architecture Levels
- Instances and Dynamism
- Practical Experience
- Initial Comparative Study
- Application in Tool Smithing
- Related Work
- Nesting, Virtual Classes, Dependent Types
- Multi-dimensional Separation of Concerns
- Modules for Alias Control
If you are interested in more than two of these …
… you’ll surely enjoy this:
|See you at|
Users of Eclipse trust in its quality. But what happens when they install additional plug-ins that are not part of the original download package? We tend to say that plug-ins only add to the installed system, but by saying so we’re actually spreading a myth:
“Adding plug-ins to an Eclipse install does not affect anything that is already installed.”
This simplest way to falsify this myth is by installing two plug-ins that together produce a deadlock. Each plug-in in isolation may work just perfect, but adding the other will cause Eclipse to freeze.
No zero-risk software install
Sure, in the absence of any correctness proofs, we can never be sure that adding one more plug-in to an already complex system will not break anything. Concurrency is just one of the most cumbersome issues to get right. Other reasons exist, why installing more is not always better.
Install-time risk assement
Actually, the p2 UI already distinguishes two kinds of installs: signed and unsigned artifacts, and warns the user when unsigned content is about to be installed. This is great, because now the user can decide whether s/he accepts unsigned content or not.
But this question is just the tip of the iceberg.
Behind the scenes of p2
When you tell p2 to install a given feature a number of things can happen that a normal user will not be aware of:
- Additional required software may be pulled in, possibly from different projects / vendors / repositories
- New features may replace existing plug-ins (patch features).
- Installation may trigger touchpoint actions that modify central parts of the system
- Installed plug-ins may access non-public classes and members of existing plug-ins
- New plug-ins may hook into the Equinox framework (adaptor hooks)
- New plug-ins can weave into the bytecode of existing plug-ins.
Is this bad?
Don’t get me wrong: I believe all these ways of influencing the installation are very useful capabilities for specific goals (and Object Teams utilizes several of those). However, we may want to acknowledge that users have different goals. If you need to be 100% sure that installing a plug-in does not delete anything on your hard disk you will not be happy to learn about the touchpoint instruction “remove”, while apparently others think this a cool feature.
Touchpoint actions have no ethical value, neither good nor bad.
Negotiation over rules
Drawing the line of how a plug-in may affect the rest of the system can be done up-front, by defining rules of conduct. Thou shalt not use internal classes; thou shalt only add, never modify etc. But that would impose a one-size-fits-all regime over all plug-ins to be published, and installations that can or cannot be created from published plug-ins. This regime would never equally suite the anxious and the daring, users with highest security constraints and users loving to play around with the newest and coolest.
Better than one size would be two sizes (conservative & bleeding edge), but then we still wouldn’t meet en par with our users, with all their shades of goals (and fears). Instead of imposing rules I suggest to enter negotiation with our users. By that I meen:
- Really tell them what’s in the boxes
- Let them decide which boxes to install
Proposing: Install capabilities
I could think of a concept of install capabilities at the core of a solution:
- Each artifact may declare a set of non-standard capabilities required for installing, which could include all items from the above list (and more)
- During install a security/risk report will be presented to the user mentioning (a summary of) all capability requests
- Based on that report a user can select which requests to accept, possibly blocking risky plug-ins from installing
- The runtime should enforce that no feature/plug-in uses any install capabilities which it didn’t declare
In this context I previously coined the notion of Gradual Encapsulation including a more elaborate model of negotiation.
More specifically I filed bug 316702. Where I’d love to read some comments.
I strongly feel it’s time to talk straight to our users: adding new artifacts to an existing Eclipse install is not always purely additive. Installation can indeed modify existing parts. In an open-source community, we should tell our users what modifications are requested, what potential risks are associated with a given artifact. They are grown-up and able to decide for themselves.