Friday, June 5, 2009

New wild idea in OOP

The beginning

A while ago, when designing a new framework, I have met some problems with the limit of current OO languages. Because it bases on the module architecture, I need a more flexible method to design and link the module to the main application. Currently, in .NET or Java, when we want to call a member from an unknown class type, we generally have to use Reflection. This method, itself, has many disadvantages. In C# 4.0 coming soon, a new feature called dynamic type is introduced. It has some advantages over Reflection. In fact, the dynamic type isn’t new concept and has occurred in many languages such as ActionScript, Python, Ruby… And it has potential to produce more error at runtime and has lower performance than static type. So may we need a new method?

In following sections, we will come through many situations which current concepts won’t provide good enough solutions. After that, I will introduce a new idea that may be worth to look. The languages are used in examples will be ActionScript and C# (these are my main development languages) but other OO languages could be similar.

The situations

Situation 1:

We want to use an object but specific type of it is unknown, or rather dynamic type. Such a object could be created by some technique such as Reflection (or getClassFromName in ActionScript) or simply from a method which get object from external lib, module, COM, Ruby, Python,…We can use the methods of it by using invoke or simply convert it to dynamic type (C# 4.0 or ActionScript 3.0). Examples:

  • Invoke (C#)
    object calc = GetCalculator();
    Type type = calc.GetType();
    object result = type.InvokeMember("Add",
    BindingFlags.InvokeMethod, null,
    new object[] { 10, 20 });
    int sum = Convert.ToInt32(result);
  • Dynamic type (C# 4.0)
    dynamic calc = GetCalculator();
    int result = calc.Add(10, 20);
  • Dynamic type (ActionScript 3.0)
    Object calc = GetCalculator();
    int result = calc.Add(10, 20);

==> What disadvantages of these :

  • Invoke technique in .NET is very powerful and it can even be used on non-public members. But it is a little complex to be used because it requires knowledge of reflection. And when you invoke a member, you have many exceptions to be aware of, because the name and arguments of member simply can’t be determined at compile- time. In many situations, you may need a simpler solution to call member of a specific type, so as, dynamic type in C# 4.0.
  • Dynamic type (C# 4.0 or ActionScript 3.0) is a simpler solution to this. But it still a big disadvantage: you can’t guarantee that what you give is valid and there isn’t a method to check a member fully before you actually work on it (you can still check name and maybe return type but checking arguments is a pain). So, you also need to catch exceptions if it happens. (Dynamic type is still a very useful when combined with other techniques such as data binding).
  • All these technique are considered slow when compared with static types.

Situation 2

We have 2 existing class CalculatorA and CalculatorB, it maybe come from different external libraries or modules. They share several methods such as Add, Subtract…which we want to use. Moreover, we want to support both of them and use them in the same code. Which class is used depends on situations. In general, if these classes both implement a specific interface or derive from a class which declares methods we need, it would be very easy. In contract, because they don’t , we have to use some techniques such as create a new wrapper which encapsulates both class or create wrappers which implement the same new interface for each class (or techniques in situations 1). These solutions need more work to do and maybe a little complex to use.

Situation 3

This one is similar to Situation 2, but there is a difference: we don’t only use some the members shared between classes but other members not shared too. For examples, CalculatorA and CalculatorB are both create on the same specification in the beginning but each class has add some different features which are valuable to be used. We can assume that CalcuatorA is provided by company A and CalculatorB is provided by company B or maybe they are the same class which came from different versions of the same library. In general, we need to check which class is currently provided and call appropriate code upon it. Because we don’t know exactly which version will be used, we may want to use some technique in Situation 1 such as Reflection to do it. What will happen if we can define two new interfaces to use with these classes in our project (for an easier life) or if you prefer another simple solution, a single interface which can be use with both classes (in this case, we may need a tool to check whether a member is supported or not) ?

Situation 4

This situation is very simple. We have a very large and complex class such as Graphics. It has many complex or annoying public members which I needn’t use. In fact, there are some interface can be used upon it but what we need is not included in one but several interfaces. So we can’t use it with a single and simple interface without creating another annoying wrapper?

Situation 5

This situation is similar. When we build an application, we find out an interesting library. We notice there are an interface X and several classes implementing it in this library. We want to use them and even want to create some new implementations of interface X. But, in fact, we don’t want to use and implement all members of X. So, we create a new interface X1 which includes subset members and implement X1. What can we do if we want to use both old and new classes with interface X1? We may have some solutions such as creating a wrapper which wraps X and implement X1, but we may prefer a simple and straightforward solution. The solution that I mention is the ability converting directly from old class to X1.

Situation 6

This situation extends Situation 5. When creating interface X1, we find out that it may be interesting if we add some new methods to X1. These extra methods may help in some situations but old classes implementing X are still useful. We want that if we use these classes and extra methods are missing, when we call them, something can be executed alternatively such as returning a predefined value, throwing a predefined exception, calling other methods of interface X1... This may be a little complex but very useful in many situations.

The new concept – View

In many OO languages, interface is an important concept. Interface concept derives from abstract class concept and provides a valuable benefit: supporting multiple inheritance. Because an interface only includes declarations of members, it can be used to separate declarations from the real implementations. We can use an interface to interact with an object without knowing its type of class, but rather only that it implements that interface. But there is a restriction, the word “implement” we use here means the class of the object must point out the interfaces implemented in its declaration. Personally, I think that it’s more natural if that class just needs implement all members which interface declares to meet the requirement. This restriction limit the usage of interface, the above situations are some examples of it. Even if we remove this restriction, the new interface won’t be good enough solutions for some situations (such as situation 6). So we may have to extend it a bit more.

I don’t want to mess with the interface concept so I create a new concept called “View”. A new “View” concept matches the interface concept basically but the new concept has some differences:

- A method (and its extended concepts such as property, index…) now can have a default behavior added to its declaration in view. A default behavior will be used when we use view on an object that didn’t implement that method. A default behavior can be: return a default value, throws a specific exception, call a sibling method or call a static method…

- In contract to interface concept, a class needn’t point out the views in its declaration to show that it will implement them. An object can be converted to a view even if its class didn’t implement all members of that view. In case a member isn’t implemented, its specific behavior will be executed instead when it’s called. If that missing member doesn’t have default behavior, the convert task will be fail.

- A view can inherits members from other views, base on interfaces and extract members from classed. It can add default behavior to members it got from them by using the fully qualified member name (Ex. IPaint.draw(), Point.move()…).

We can only call a member from a view if the view has declaration of it, so no more invalid member name or argument problems at runtime. If we convert a view holding a object to another type, all extra member won’t be considered.

More definition of View

After receiving some comments, I think that'll be more clearly to give more defifinition here.
So what is a view?

Meanwhile a class encapsulates an entire entity type (the state and behavior),a view encapsulates the signatures (or declarations) of some behaviors sharing between entities. Those behaviors are what we want to use on those entities and when a behavior is missing on an entity, we could use the standard reaction of behavior instead. In some respect, it's similar to mask. Using a view on an object is similar to apply a mask on it.

Comparison to Duck-typing

One of replies I received points out the similarity between my idea and a principle called duck-typing. You can read its definition here: http://en.wikipedia.org/wiki/Duck_typing.
After reading about duck-typing from wiki link, I notice the phrase "in computer programming, duck typing is a style of dynamic typing..". In fact, duck-typing is more raw and powerful because it allow you convert a class to any compatible class or interface. I think it and dynamic type are the same, though it could check the compatibility before using : "With Duck Typing, in order to prevent strange, hard to detect errors, the developer needs to be aware of each potential use of the method 'press', even when it's conceptually unrelated to what he or she is working on".
My idea is more restrict, may be derived from duck-typing interface, but the power is still remained fully. We use "view" when we need (instead of any compatible class or interface to be confused) and the compatibility is checked (fully) when converting. The view is similar to interface so it's more understandable when using than duck-typing: "In essence, the problem is that, 'if it walks like a duck and quacks like a duck', it could be a dragon doing a duck impersonation. You may not always want to let dragons into a pond, even if they can impersonate a duck". Human and monkey have some similar aspect but when you use monkey in place of human is something strange (hard to understand though it give you more power!).
A question: How is my idea any different? Could a dragon still impersonate a duck my suggestion? which means compromises type safety?
According to my idea, a view is a collection of behaviors's signatures, not a real entity type, those behaviors are what we want to use on an object if it has. In the "duck" example, we could define a view such as VDuckBehavior and use it to interact with some dragon if possible. Similarly, we may include some behaviors of human in a view (ex: viewX) that monkey also have (certainly they are not the same).
As you see, although they are similar, their principle are different (A similar situation we all know: abstract class, interface and multi-inheritance, do you agree?). Moreover, view have more features that may be useful, specially default behavior.
Duck-typing and dynamic type could make a design difficult to understand if they're used. I don't have a clear interface definition so comparing view with duck-typing interface may be not obvious. But at least, a view has more clear meaning (interface is derived from abstract class) so it can be add more features such extracting behaviors from class and default behavior. The view concept and class concept are independent to each other. Meanwhile the interfaces are needed to exist before creating class, the view is generally created after that. Comparing to duck-typing, view is easier to understand and has not violate OO principles.
If the future C# support duck-typing, basic programmer could have a hard time to understand it after learning about OOP and you could not add extra features from view concept (to make more complicated).

Example view for C#

We have following classes and interfaces:

namespace NewConcept.Examples
{
interface ISimpleCalcuator
{
int Add(int a, int b);
int Subtract(int a, int b);
}
}

namespace NewConcept.Examples
{
public class CalculatorA: ISimpleCalcuator
{
public CalculatorA()
{
}

#region ISimpleCalcuator Members

public int Add(int a, int b)
{
return a + b;
}

public int Subtract(int a, int b)
{
return a - b;
}

#endregion

public int Multiply(int a, int b)
{
return a * b;
}
}
}

namespace NewConcept.Examples
{
public class CalculatorB:ISimpleCalcuator
{
public CalculatorB()
{
}

#region ISimpleCalcuator Members

public int Add(int a, int b)
{
return Math.Abs(a + b);
}

public int Subtract(int a, int b)
{
return Math.Abs(a - b);
}
#endregion

public int Divide(int a, int b)
{
if (b == 0)
return 0;
else
return (int)Math.Abs(a / b);
}
}
}

We have two classes implementing the same interface ISimpleCalculator. Each class also has its own method which didn’t declare in the interface. We want to use all the methods if possible so we create a new view.

namespace NewConcept.Examples
{
public view VCalculator bases on ISimpleCalculator
{
int Divide(int a, int b) default return 0;
int Multiple(int a, int b) default throw new Exception("not support");
}
}

The VCalculator view has declarations basing on ISimpleCalculator. It also add to more methods with default behavior. The first method will return 0 at default when the second will throws an exception. Then we may use it like this:

VCalculator viewA = new CalculatorA() as VCalculator;
if (viewA != null) //convert sucessfully
{
int c = viewA.Add(2, 3);
int d = viewA.Multiple(2, 3);
}
VCalculator viewB = new CalculatorB() as VCalculator;
if (viewB != null) //convert sucessfully
{
int c = viewB.Add(2, 3);
int d = viewB.Divide(2, 3);
}

The VCalculator can be converted from either CalculatorA or CalculatorB instances. After the conversion, the view can be used easily with some notices to methods which have default behavior.

Example view for ActionScript

We use the same classes as above but implemented in ActionScript. First is a simple interface two following classes implement it.

package Examples
{
public
interface ISimpleCalculator
{
function Add(a:int, b:int):int;
function Subtract(a:int, b:int):int;
}
}

package Examples
{
public class CalculatorA implements ISimpleCalculator
{
public function CalculatorA()
{

}

/* INTERFACE Examples.ISimpleCalculator */

public function Add(a:int, b:int):int
{
return a + b;
}

public function Subtract(a:int, b:int):int
{
return a - b;
}

/* Extend */
public function Multiply(a:int, b:int):int
{
return a * b;
}
}
}

package Examples
{
public
class CalculatorB implements ISimpleCalculator
{

public function CalculatorB()
{

}

/* INTERFACE Examples.ISimpleCalculator */

public function Add(a:int, b:int):int
{
return Math.abs(a + b);
}

public function Subtract(a:int, b:int):int
{
return Math.abs(a - b);
}

/* Extend */
public function Divide(a:int, b:int):int
{
if (b == 0)
return 0;
else
return int(Math.Abs(a / b));
}
}
}

Each class also has its own method which didn’t declare in the interface. We want to use all the methods if possible so we create a new view.

package Examples
{
public view VCalculator bases on ISimpleCalculator
{
function Divide(a:int, b:int):int default return 0;
function Multiple(a:int, b:int):int default throw new Error("not support");
}
}

The VCalculator view has declarations basing on ISimpleCalculator. It also add to more methods with default behavior. The first method will return 0 at default when the second will throws an exception. Then we may use it like this:

var viewA:VCalculator = new CalculatorA() as VCalculator;
if (viewA != null) //convert sucessfully
{
var c:int = viewA.Add(2, 3);
var d:int = viewA.Multiple(2, 3);
}
var viewB:VCalculator = new CalculatorB() as VCalculator;
if (viewB != null) //convert sucessfully
{
var c:int = viewB.Add(2, 3);
var d:int = viewB.Divide(2, 3);
}

The VCalculator can be converted from either CalculatorA or CalculatorB instances. After the conversion, the view can be used easily with some notices to methods which have default behavior.It’ could be more interesting if we get CalculatorA and Calculator B from external modules and use it easily with internal VCalculator.

Some more features

Here are some more features that could be added to new View.

- Checking a member is implemented or not. Example: we can use view.isImplemented(“memberName”).

- Force a method to execute standard behavior. Example: view.forceStandard(“memberName”).

How could the new view concept help us

First, the new View can provide new good solutions to many situations such as above ones. Now we have a method that could be safer, easier and have better performance to access an object with unknown class type. That would help plugin programming easier. If it’s supported, the new view would change the way we code a bit. Instead of using interface or dynamic type, we can use view as a better solution in some situations.

Everyone please give your thought about new ideas: Do you think it’ll be useful or not? Other situations you would love to use it? Do you want to extend it more?

A little wish

After reading to here, I wish that you would think that my idea is somewhat that’s interesting and worth to have a look. As I said, C# and ActionScript is my main development languages so it’s wonderful if these languages support it in the next versions. Other languages would be great, too.

BS. I added a poll so please take it if you come to here.


4 comments:

  1. Another discussion with many professional at Lambda the Ultimate: http://lambda-the-ultimate.org/node/3447

    ReplyDelete
  2. I have only skimmed through but it sounds simply like structural subtyping like you can find it in O'Caml and AFAIK Scala allows it, too.

    ReplyDelete
  3. Your ideas must be implemented with late biding, so it is slow and offers no performance gain comparing to other solutions. You may search Google about LinFu, it has all the features you described here

    ReplyDelete