środa, 28 października 2009

ASD Polemic

This post is a polemic with
this. This is why it's in English.

Just to provide enough context, I'll quote a lecture slide (I hate you for not doing this in your post; I've wasted so much time trying to figure out what do you mean, e.g. what is the factory supposed to create or where is your point of view in the system that you develop).
The lecture slide says:
"
In some situations, using the new keyword to construct
new objects of a given class is NONO
Connection con = new Connection(db);
This suffers from tight coupling. If, later on, you want
to issue connections from a pre-existing pool of
objects, rather than creating a new one every time, this
code prevents you. Instead, use a 'factory' approach:
Connection con = Connection.get(db);
"
First lets make things clear.
First of all note that this example is there to making people aware that there exists a thing called a factory method and that it may help when sharing objects. When an noob reader reads this slide, he grasps those two ideas and everything's OK, because those general ideas are OK. On the other hand, when someone who really knows something about those things read this, he will almost certainly say "ugh!", because it is not a good example of factory. This is a bit useless pattern called factory method, because:
1. This method is defined when the Connection class gets written, noone can change it later
2. Noone can create objects of the subclasses of Connection (assuming constructors are private).
3. It may give you the same object many times when you don't want that.
4. It is a static method so it in general cannot rely on having any information about context in which it is called.

The example on the slide is really bad because a method of class A creates objects of class A, which makes the problems 1 and 2 very true, so it is even worse than using the constructor directly. If it was another class, which would allow this method to be non-static, you could probably inherit from it, redefine the method and get the desired behaviour.
ASIDE: Note that I do not say "using new operator", but "using constructor" to demonstrate how much I dislike the fact that the lectures are so Java-oriented.

What overcomes all those problems is the abstract factory pattern.

After a tough but thorough analysis of your stream of conciousness I arrived at a conclusion that you are actually arguing agains the above constraints of the factory method pattern.
You correctly argue with the problems 1 and 3 in about half of your post, starting with:
"For example, say you have a database connection object and you want to set up a pool of X objects that you want to reuse. Rather than use the factory method, you could edit the Connection object such that is composed of e.g. a DatabaseSocket which interfaces with the database directly. Connection's constructor grabs a DatabaseSocket from the pool, and existing methods on the Connection object forward themselves to the DatabaseSocket. (delegation!)"
You basically found an example in which you need to modify the way Connection is defined with (pardon my English (as French say) if it's not a comprehensible sentence). Additionally, you could probably have "interface Connection" and a subclass of it defined to solve your specific problem with multithreading. This would overcome problem 3.
You argue with the problem 4 in:
"Consider the case where there is contention for a limited resource. With a factory method that directly hands you an object, the onus is usually on you to properly handle sharing and locking for that object."
A proper object oriented factory could handle that because you would define the way of creating objects and you could use available information about how to share and lock particular objects. You would just give that knowledge as an argument to the constructor of the factory. Now I should describe the abstract factory design pattern but other people have done it before, so I will not repeat the effort. You can find it on Wikipedia.

"'do you want a brand new object of your very own, or are you happy being given an object to achieve what you want?' If you're doing anything with mutable state outside your program logic, you probably don't need to track any extra data outside of whatever exists externally, and you should ask whatever subsystem you are dealing with for appropriate objects to this effect. However, if you want additional data with your ice-cream sundae, the former is actually a sensible thing to do, and doesn't preclude you from changing the implementation of the created class so that it grabs the 'real' object elsewhere behind the scenes."
Very true. However, note that with the abstract factory you can pretty much have the advantages of both things, so if any one of the above solutions is too limited, there is another way!
To conclude, you are generally right, but you are a bit cherry-picking a particular example from the slide and you are missing the point a bit, as you praise a normal idea agains a bad one, when there is a really good one (the abstract factory), that solves what the bad one was supposed to solve.

---
Different issue:
"Inheritance has its uses, but you need to be careful about what it really means. It's not about extending functionality as much as it is about telling people "Hey, I have a cool object here with special features I can use, but since it behaves exactly like this more basic object, you guys get to play with the same instance as me!"."
So you are talking about extending the inheritance hierarchy upwards (introducing another subclass such that "you guys" (i.e. another code) can start using the object). This is one way of taking advantage of inheritance and it is a very good one because we build up the hierarchy after we actually have an implementation of a base class. Therefore we know what polimorphic methods will be needed and what are necessary generality constrains of the base class to make the implementation feasible. This usually leads to introducing something that Java calls an interface. Note, however, that if a class is really designed to extend it (e.g. because it is part of a framework), then extending functionality can be a good idea as well. It is really important, however, to distinguish between extending existing functionality and/or data (not a good idea in most cases) and implementing interface.
The main problem of inheritance is CONFUSION, MISUSE and MIXING of those two ideas. Using any one of them is not a problem. The whole idea of inheritance is also not a big problem, on the contrary to what Ian tries to squash into students' minds (Yes, I know that inheritance is sometimes inflexible because you cannot add methods to base classes, but this is not the argument that Ian uses. At least he stresses it an order of magnitude weaker than all the other arguments).

The lecture slide says:
"The extends word wrongly suggests "add more fields
to" or "reuse the implementation of""

I think that there is a big problem with naming, because "inheritance" really does not describe a result of using a programming concept, but way of implementing both of the two concepts described above by a programming language. That's why in C++, which is quite old now, there is syntactically just one way of inheriting (the ":" operator; OK, you can write "private", "protected" or "virtual" after it but nobody uses the first two and the third is broken). In Java, which is a bit younger, there is a separation of extending classes and implementing interfaces. There is a problem with the extending mechanism, particularly with the illusion of being able to extend anything and override every method of it, but the "extends" keyword is a good word. It reminds the writer that it whatever is extended should really be extendable. In my opinion it is more like telling the coder: "You are going to EXTEND something. Are you sure?" rather than "You are writing 'extends', so whatever you are trying to extend must be designed for extending.". It's silly, why would anyone think something like this? There is the problem with how easy it is to write "extends" that compiles, not with the word itself. This is a problem with Java, not with extending.

Thanks for the paragraph about traits!

1 komentarz:

  1. I am glad you mentioned the abstract factory pattern. I agree, it is a very useful concept, and I considered mentioning it as a 'good' example - but
    (1) if you need an abstract factory, and your design goal is to make things as general as possible, this pattern should be obvious
    (2) the lecture spoke specifically of factory and gave a useless/confusing example, so I decided to concentrate on that.

    Strangely, some languages like Objective-C use class methods in place of constructors, so they can construct an object of a different type to the one you asked for. I am not convinced this is a good idea - you have no guarantee that the object you constructed is the object you asked for.


    Regarding inheritance being inflexible, sometimes this is a feature! But changing a base class is often very convenient, which is why C# has extension methods, Ruby has monkey patching, etc.

    I forgot to mention in my post that JavaScript has a completely different form of OOP called prototypes, but I am still unsure whether I like it or not :) Nobody could call it inflexible, however.


    Thank you for replying!

    OdpowiedzUsuń