Updating Object Properties While Respecting Encapsulation

This article is a follow-up to my previous article Accessing Object Properties While Respecting Encapsulation. In that article I looked at making object properties accessible while respecting encapsulation. In this article I will look at how to design operations on objects in order to update property values while respecting encapsulation – that is, without resorting to the all too common “setter” (otherwise known as a “set” method).

By the end of my previous article we had the following:

        class Circle
        {
            public uint Radius
            {
                get { return radius_ ; }
            }

            // ...

            private int x_centre, y_centre;
            private uint radius_;

            // ...
        }

I used this fragment of a C# class to make the point that our approach should be to decide what properties we want to make publicly readable, and then decide how we’re to implement them; If the implementation just happens to be returning a data member, then that’s fine – but that is a private matter for the class.

Moving on, it seems reasonable that users of this class would also want to be able to update the size of the circle. Therefore, should we make it possible to assign to the Radius property? The answer is simple – NO! How then, can we make it possible to update a Circle object’s size?
To understand this, it is worth stepping back and looking at what we actually get from object oriented programming. In this case, I’m thinking specifically of how objects help us to represent the real world. With this in mind, when tempted to make an object property “settable”, we should stop for a minute and think about what we really want to achieve – and what we want to achieve is not the setting of the Radius property, but the resizing of the Circle object. Therefore, we should endeavour to say just that by having a Resize() method. The class now looks like this:

    class Circle
    {
        public uint Radius
        {
            get { return radius_ ; }
        }

        public void Resize(uint new_radius)
        {
            radius_ = new_radius;
        }

        // ...

        private int x_centre, y_centre;
        private uint radius_;

        // ...
    }

Here is a sample usage code fragment:

    class UsesCircle
    {
        static void Example(Circle the_circle)
        {
            uint new_radius = 42;
            the_circle.Resize(new_radius);
             //...
        }

        //...
    }

By designing the class’ operations with the real world in mind, once again we have code that speaks to its reader in more natural manner, and once again it achieves this simply by bearing in mind how the operation would be expressed in English. Looking back for moment, let me point out the question posed above: how can we make it possible to update a Circle object’s size? It is worth noting how simply asking the right question provided a strong clue to its answer!

Moving on, I now want to leave my Circle class example for the time being, and move on to emphasise another important point: there are many cases where it would actually be ridiculous to make a property value explicitly “settable”! My favourite example is a class to represent a bank account. First, just to strengthen the point, let me show this done the wrong way:

        class BankAccount
        {
            public long Balance
            {
                get { return balance_; }
                set { balance_ = value; }
            }

            // ...        

            private long balance_;

            // ...
        }

The point is this: do you know of a bank that will let you set your balance? If so, please get in touch and let me know which one! Real world bank accounts allow the user to do the following:

  • Query their balance
  • Make deposits
  • Make withdrawals

Therefore the operations on the BankAccount class should reflect this. Here is a fragment illustrating the design done sensibly:

        class BankAccount
        {
            public long Balance
            {
                get { return balance_; }
            }

            void Deposit(uint amount)
            {
                balance_ += amount;
            }

            void Withdraw(uint amount)
            {
                balance_ -= amount;
            }

            // ...        

            private long balance_;

            // ...
        }

Note that BankAccount could dispense with its Withdraw() method, and instead allow Deposit() to accept a negative amount. Which is the most natural approach rather depends on the application area in which BankAccount is being used, and it serves to illustrate the existence of alternatives.

That bring this matter to a conclusion. This article and its predecessor have, out of necessity, presented quite simple examples. Sadly, in reality and in practice, software development is far from simple. However, I hope the two articles will contribute to the clearing up of misunderstandings that appear to remain all too common.

Leave a Comment

Your email address will not be published. Required fields are marked *