http://www.codinghelmet.com/  

Wear a helmet. Even when coding.

howto > how-to-avoid-the-need-to-implement-icloneable-interface-and-what-to-do-instead

How to Avoid the Need to Implement ICloneable Interface and What to Do Instead
by Zoran Horvat @zoranh75

Questioning the Role of the ICloneable Interface

Many programmers ask themselves what is the purpose of the ICloneable interface. This interface comes with only a single method – Clone – and it is supposed to create a copy of the host object:

interface ICloneable
{
    object Clone();
}

This interface, however simple, comes with a couple of issues:

  • It is unclear whether the Clone method will create a deep or shallow copy
  • Return value is System.Object and it requires downcast to access concrete features of the resulting object

Each of these problems alone is sufficient to give up the ICloneable interface entirely.

The first issue, being unable to tell whether deep or shallow copy will be made, is of semantical kind. We strongly dislike interfaces which do not communicate intention clearly enough. Such interfaces require the programmer to read concrete implementation before deciding how to write the client-side code. Not only that it will take additional time to get acquainted with the cloneable class, but it also couples client implementation with implementation of the concrete cloneable class. Change made to either side without changing the other will potentially introduce a defect in code.

The second issue, return type, is more of a syntactical kind. We strongly dislike methods that return weak types such as System.Object or System.String. We want to see objects of concrete types, so that we can access their concrete members – methods or properties.

An Example That Will Expose the Problems with ICloneable

Take a look at this simple class:

class Person : ICloneable
{
    public string Name { get; }
    public string Surname { get; }

    public Person(string name, string surname)
    {
        this.Name = name;
        this.Surname = surname;
    }

    public Person(Person other):
        this(other.Name, other.Surname)
    {
    }

    public object Clone()
    {
        return new Person(this);
    }
}

This class is modeling a person, identified by first and last name. Person class implements ICloneable and it just returns a new Person object with same name and surname.

On a related note, Clone method is implemented via a copy constructor. It is a good practice to equip the class with a copy constructor in any situation in which making copies of objects is a requirement.

But now, the fundamental question: What can we do with an object of Person class that we couldn’t do without implementing the ICloneable interface. The only viable answer that comes to my mind is representing an object as an object implementing the ICloneable interface alone:

void DoSomething(ICloneable obj)
{
    object clone = obj.Clone();
    // Now do something with clone
}

In this case, we have a method which expects an object implementing ICloneable. It does not expect an object of class Person, because that would be too specific for this method. The only thing this method wants to do is to clone an object and then deal with the clone, like send it over the wire or persist it in the database.

But then, we immediately see the problem. Clone method has returned a plain object. DoSomething function has no idea what concrete type of this object is.

Bottom line is that having ICloneable interface didn’t bring us any benefits. At the level of the Person class we didn’t need it because we already have the copy constructor which is basically doing the same thing as the Clone method. (Only better, because the result of calling the copy constructor is a strongly typed Person object, while the result of calling the Clone method is System.Object.)

On the consuming side, we didn’t have much benefits either. We could narrow down the type of input argument of the DoSomething function, and that is really nice. DoSomething function doesn’t have to depend on the Person class anymore. But then, the object it will obtain through calling the Clone method is just a System.Object and it is quite useless in most of the cases.

Even if DoSomething function attempted to send the cloned object over the wire, the other big issue appears. Is it the deep or the shallow copy then? Is it safe to pass the object to any other object after cloning? I mean, it could happen that some references contained in the cloneable object were just copied to the cloned instance in a shallow copy manner.

That could easily cause instability. Maybe this particular scenario required deep copy of those references? We cannot tell. That is the serious issue, because ICloneable interface doesn’t give any concrete promises and then we cannot make concrete decisions down the stream.

Bottom line, ICloneable is almost of no use to both the feature provider and the feature consumer. We have to look for a more promising solution.

Replacing ICloneable with a Func Delegate

One reasonable replacement for the ICloneable interface is Func<T> delegate. This delegate type indicates a function returning a type T. Nothing more than that. Let’s see how the DoSomething method would look like when Func delegate is applied:

void DoSomething(Func<Person> deepCopy)
{
    Person clone = deepCopy();
    Send("name", clone.Name);
    Send("surname", clone.Surname);
}

This time, the client could enforce a concrete Person type. It was of great importance to the client, because it wanted to send name and surname over the wire. This time, the client can get hold of the concrete Person class instance and then access its concrete Name and Surname property getters.

On the calling side, we can easily wrap an object in the closure of a lambda:

Person person = new Person("Max", "Planck");
DoSomething(() => new Person(person));

As you can see, I was perfectly capable to produce a Func delegate that will create a deep copy of the Person object. Once again, I have relied on the copy constructor, which might not be the best solution in all cases. Again, we have that question – is it deep or shallow copy that the client is asking for.

Let’s look at the client then. The DoSomething method is expressing its needs through the argument name: deepCopy. Func delegate expected by the DoSomething method implies deep copy requirement. Not a hundred per cent satisfying solution, but better something than nothing. This solution is based on a convention.

Finally, there is this problem of deep vs. shallow copy in the Person class. Copy constructor is not the best solution when the client is the one who tells which kind of copy should be made. It is better to replace it with a static method with a name indicating the intention:

class Person
{
    public string Name { get; }
    public string Surname { get; }

    public Person(string name, string surname)
    {
        this.Name = name;
        this.Surname = surname;
    }

    public static Person DeepCopy(Person other)
    {
        return new Person(other.Name, other.Surname);
    }

}

This is all it takes to support copying convention both on the supplying and on the consuming end.

Summary

In this article we have tackled the question of usefulness of implementing the ICloneable interface. We have seen that this interface doesn’t bring much revenue back after we implement it.

Instead, it may be a better idea to replace it with the Func<T> delegate, where T is the concrete type the consumer needs after the copy is created. In this way, we can satisfy the client and then implement the supplying end appropriately, i.e. supplier will adapt to the desired interface of the consumer.

This solution is not the best possible, because there is still some uncertainty regarding whether deep or shallow copy has to be created. We have suggested that naming convention should be applied to clarify the situation.

See also:

Published: Jun 21, 2016

ZORAN HORVAT

Zoran is software architect dedicated to clean design and CTO in a growing software company. Since 2014 Zoran is an author at Pluralsight where he is preparing a series of courses on object-oriented and functional design, design patterns, writing unit and integration tests and applying methods to improve code design and long-term maintainability.

Follow him on Twitter @zoranh75 to receive updates and links to new articles.

Watch Zoran's video courses at pluralsight.com (requires registration):

Making Your C# Code More Object-Oriented

This course will help leverage your conceptual understanding to produce proper object-oriented code, where objects will completely replace procedural code for the sake of flexibility and maintainability. More...

Advanced Defensive Programming Techniques

This course will lead you step by step through the process of developing defensive design practices, which can substitute common defensive coding, for the better of software design and implementation. More...

Tactical Design Patterns in .NET: Creating Objects

This course sheds light on issues that arise when implementing creational design patterns and then provides practical solutions that will make our code easier to write and more stable when running. More...

Tactical Design Patterns in .NET: Managing Responsibilities

Applying a design pattern to a real-world problem is not as straight-forward as literature implicitly tells us. It is a more engaged process. This course gives an insight to tactical decisions we need to make when applying design patterns that have to do with separating and implementing class responsibilities. More...

Tactical Design Patterns in .NET: Control Flow

Improve your skills in writing simpler and safer code by applying coding practices and design patterns that are affecting control flow. More...

Writing Highly Maintainable Unit Tests

This course will teach you how to develop maintainable and sustainable tests as your production code grows and develops. More...

Improving Testability Through Design

This course tackles the issues of designing a complex application so that it can be covered with high quality tests. More...

Share this article

webmasters