tags:

views:

107

answers:

1

The "question" here is a general discussion topic.

It's pretty well known that ICloneable has serious problems. Purely immutable objects are trivially cloneable (by reference), so it's safe to assume that the interface for cloning an object deals with mutable objects. One place where I've seen the concept of a cloneable object be extremely useful is in combination with using freezable objects for threading and/or performance benefits. IMO, the Freezable class from WPF is a great example of this pattern, but is too application specific:

  • Problems with treating System.Windows.Freezable as general-purpose:
    • It's a class instead of an interface, which limits its scope
    • It's derived from DispatcherObject and DependencyObject, both of which add domain-specific overhead
    • Adds a dependency on WindowsBase.dll, which is not exactly lightweight way to pick up what would be a small interface

In the spirit of the Freezable type, I believe the following IFreezable interface is broadly applicable to modern code.

  • A few design decisions I made:
    • To assist in the usage of this interface in multithreaded environments, the freezing operations are designed to not throw an InvalidOperationException if CanFreeze is false. However, no explicit guarantees of thread safety are included in the interface (these are left to implementers).
    • The Changed property from Freezable class is not included - use INotifyPropertyChanged and/or INotifyCollectionChanged instead.

Here is the code:

/// <summary>
/// Defines an object that has a modifiable state and a read-only (frozen) state.
/// Classes that implement IFreezable can be made immutable, and can clone
/// themselves.
/// </summary>
public interface IFreezable
{
    /// <summary>
    /// Gets a value that indicates whether the object can be made unmodifiable.
    /// </summary>
    bool CanFreeze
    {
        get;
    }

    /// <summary>
    /// Gets a value that indicates whether the object is currently modifiable.
    /// </summary>
    bool IsFrozen
    {
        get;
    }

    /// <summary>
    /// Creates a modifiable clone of the IFreezable, making deep copies of the
    /// object's values.
    /// </summary>
    /// <returns>
    /// A modifiable clone of the current object. The cloned object's IsFrozen
    /// property is false even if the source's IsFrozen property is true.
    /// </returns>
    IFreezable Clone();

    /// <summary>
    /// Makes the current object unmodifiable and sets its IsFrozen property
    /// to true.
    /// </summary>
    /// <returns>true if the object is made frozen, otherwise false.</returns>
    bool TryFreeze();

    /// <summary>
    /// Creates a frozen copy of the IFreezable. Because the copy is frozen, any
    /// frozen sub-objects are copied by reference.
    /// </summary>
    /// <param name="freezable">
    /// Upon return, this is set to a frozen copy of the IFreezable. If a frozen
    /// copy could not be made, the value is set to null.
    /// </param>
    /// <returns>
    /// true if the current instance is frozen or if a frozen copy of the
    /// instance could be made, otherwise false.
    /// </returns>
    bool TryGetAsFrozen(out IFreezable freezable);
}
A: 

Isn't the Builder Pattern a better solution to the same problem? By better, I mean that it makes more sense to me to have a mutable class that generates an immutable one than try to create both pieces of functionality in one place.

pdr
The problem here is you can't take a thread-safe immutable object and use it to create a mutable instance unless you can clone the builder, which leads back to the original issue with ICloneable.
280Z28