9

I am learning OOP. I have studied much about encapsulation but the more I read the more I became confused.

I understand we hide (by making private) data and expose it to user of class (other developers) as properties or methods. I also understand by encapsulation we hide details.

In an article (http://www.csharp-station.com/Tutorial/CSharp/lesson19) I read:

Abstract from article

When designing an object, you must think about how others could use it. In a best-case scenario any program using the object would be well designed and the code would never change. However, the reality is that programs do change often and in a team environment many people touch the same code at one time or another. Therefore, it is beneficial to consider what could go wrong as well as the pristine image of how the object should be used.

In the case of the BankAccount object, examine the situation where code outside of your object could access a decimal Amount field or a string CustomerName field. At the point of time that the code is written, everything would work well. However, later in the development cycle, you realize that the BankAccount object should keep track of an int CustomerID rather than string CustomerName because you don't want to duplicate relationships between information (or some other valid reason to alter the definition of internal state). Such changes cause a rippling effect in your code because it was built to use the BankAccount class, as originally designed (with CustomerName being a string), and you must now change code that accesses that state throughout your entire application.

The object-oriented principle of Encapsulation helps avoid such problems, allowing you to hide internal state and abstract access to it though type members such as methods, properties, and indexers. Encapsulation helps you reduce coupling between objects and increases the maintainability of your code.

Question How will encapsulation help when making changes in code and from its rippling effects. For a data member, if I change its type from int to float, (even if I am exposing this using property) I will need to change variable type where I am using already using this code.

Kindly guide me how encapsulation will help with such changes.

Thanks for this help and guidance.

6 Answers 6

9

How will encapsulation help when making changes in code and from its rippling effects. For a data member, if I change its type from int to float, (even if I am exposing this using property) I will need to change variable type where I am using already using this code.

The benefit of encapsulation is that it lets you change the internal implementation without breaking client code. It doesn't protect you if you decide that you need to change the interface to your code, but that's a different matter.

Example: Say you have a value representing the price per unit of some commodity. The price is expressed in cents, and because you don't deal in fractional cents you decided to make the property an integer (I'll use C here because I'm not very familiar with C#):

int _price

int pricePerUnit(void) {
    return _price;
}

int priceForUnits(int units) {
    return units * _price;
}

That all works out fine until one day when somebody notices that your firm is losing a lot of money due to rounding errors. Many of the commodities that you track are bought and sold in lots of many thousands of units, so you need to start tracking the price to an accuracy of at least 0.001 cent. Because you were smart enough to encapsulate the price instead of letting clients access it directly, you can make that change pretty quickly:

double _dprice

int pricePerUnit(void) {
    return (int)_dprice;
}

int priceForUnits(int units) {
    return (int)(units * _dprice);
}

The interface that clients use to obtain prices stays the same, but the data they get back is now more accurate. If the price per unit is $1.001, priceForUnits(1000000) will now return a price that's $1000 greater than before. That happens even though you haven't changed the interface to your system at all, and you therefore haven't broken any client code.

Now, that may not always be all that you need to do. Sometimes you'll need to change or augment your interface so that you can report the price more accurately to clients, too:

double pricePerUnit() {
    return _dprice;
}

A change like that will break client code, so you might instead keep the old interface and provide a newer, better routine:

int pricePerUnit() {
    return (int)_dprice;
}

double accuratePricePerUnit() {
    return _dprice;
}

You and the rest of your team can then embark on the process of converting all the clients of your system to use the newer, better accuratePricePerUnit(). The client code will get more accurate as you make progress on that task, but even the old stuff should continue to work as well as it did in the past.

Anyway, the point is that encapsulation lets you change the way the internals work while presenting a consistent interface, and that helps you make useful changes without breaking other code. It doesn't always protect you from having to update other code, but it can at least help you do that in a controlled manner.

0
3

In my experience, encapsulation makes doing the "wrong" thing much more difficult. You can group together functionality which semantically go together and isolate them from functionality which can lead to bad or unpredictable behavior. It can also help hide the details from the end user which may help increase safety and reliability.

Consider this post by John D Cook. Consider that you have a Bread object. One natural thing to slice this bread. So you write a slice() function, so you can do

slice(loaf)

with a new loaf object you've created. This makes sense. But if you're not careful, you might accidentally call

slice(finger)

with a finger object somewhere in your project. This could lead to very bad things. Instead, encapsulate this function/method into a Bread class so you can do this

loaf.slice()

This definitely helps avoid calling finger.slice() accidentally, since finger probably doesn't have a slice() method associated with it.

This is a bit of a contrived example but I've found it helpful. Encapsulation is can sometimes be an underrated aspect of OOP, but it's a good one.

2

Encapsulation helps large groups of developers coordinate their work more efficiently. Each group of developers works on different modules, and those modules are encapsulated -- separate the code into a small number of widely available operators whose use they can't control tightly, and a large number of internal operators that the can control tightly. What this means is that each group of developers can define invariants that are important for their module to maintain, and make sure those invariants hold regardless of what other modules' developers do.

Since encapsulation allows for invariant-preservation, it is often used to maintain security/safety invariants, e.g.

  • that a datastore is never accessed without proper credentials
  • that a bank account never has more money added than was removed from another
  • that certain operations are always logged

The Object capability model is a methodology for writing security sensitive code that is encapsulation on steroids.

The security model relies on not being able to forge references; see Synthesizing addresses of actors.

Objects can interact only by sending messages on references. A reference can be obtained by:

  1. initial conditions: In the initial state of the computational world being described, object A may already have a reference to object B. parenthood: If A creates B, at that moment A obtains the only reference to the newly created B.
  2. endowment: If A creates B, B is born with that subset of A's references with which A chose to endow it.
  3. introduction: If A has references to both B and C, A can send to B a message containing a reference to C. B can retain that reference for subsequent use.

In the Object-capability model, all computation is performed following the above rules.

Advantages that motivate object-oriented programming, such as encapsulation or information hiding, modularity, and separation of concerns, correspond to security goals such as least privilege and privilege separation in capability-based programming.

2

How will encapsulation help when making changes in code and from its rippling effects.

Let me give you a typical and simplistic example. First, suppose you are not using encapsulation: You have a set of data and use an array to store that data, and there is another part of your program that uses that array. Now, if at some point you decide that a linked list is a better choice for storing your data. If you replace array with linked list, what is going to happen? Your program will break, unless you make changes all over the place to replace the array-processing logic with linked-list-processing logic.

But, if you use OO/Encapsulation, then you probably partition your program into classes, one that store the data, and other, that uses the data. In the first class, you hide your implementation (encapsulation), and expose your services through methods like

size()
remove(int index)
add(int index, Object o)
get(int index)

...

In this second case, if you change the implementation of the storing class from array to linked list, or to any other thing, it will not affect your clients. No ripple effect.

1

The main way that encapsulation helps reduce rippling effects of change is by keeping as many of the implementation details private to the class. By limiting the interface only to those members needed to use the class, many changes can be made to the implementation without affecting any code that uses the class.

That seems to be the point being made by the text you quoted, although I also found it a little confusing on my first read.

1

I think it allows you to change the functionality of your class without, although not always, introducing BC breaks (Binary or Behavioural Compatibility). That is, you can change how your class does 'something' without having to change how the end user tells it to do that 'something'.

The example you gave of changing the return type is a BC break, because anyone who previously used your class will not be able to use the new version without recompiling. This is something that should be done only as a last resort, and in a professional setting can only be done with clearance from architects and after paperwork has been filed, customers notified, etc.

It also gives you complete control over the state of your object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.