I would definitely make this an interface. The reason being is that, interfaces are supposed to be very general, and that's why we can implement multiple interfaces. If there is some boiler-plate code you want to write, there's nothing stopping you taking advantage of both interfaces and abstract classes.
public interface ICustomCloneable<T>
{
T ShallowCopy();
T DeepCopy();
}
public abstract class CustomCloneable<T> ICustomCloneable<T> where T : class
{
public T ShallowCopy() { return ShallowCopy(this); }
public T DeepCopy() { return DeepCopy(this); }
// static helpers
public static object ShallowCopy(T obj) { /* boilerplate implementation */ }
public static object DeepCopy(T obj) { /* boilerplate implementation */ }
}
public class ClassA : CustomCloneable<ClassA> { /* Use boilerplate functionality */ }
public class ClassB : SomeOtherClass, ICustomCloneable<ClassB>
{
// implement ICustomCloneable using static helpers
public ClassB ShallowCopy() { return CustomCloneable<ClassB>.ShallowCopy(this); }
public ClassB DeepCopy() { return CustomCloneable<ClassB>.DeepCopy(this); }
}
I've used generics here, but there's no reason you need to... It might even be desirable not to. This approach allows you to write boilerplate code, but not be tied down by it:
public class ClassC : ICustomCloneable<ClassC>
{
public ClassC ShallowCopy() { /* Do special cloning for ClassC */ }
public ClassC DeepCopy() { /* Do special cloning for ClassC */ }
}