Recently, a flaming post went up on the DZone.com website. A Java fellow posted a blog in which he bluntly stated that he would much rather employ Java programmers than C# programmers. The absolute point of the piece is that AOP is extremely popular in Javaland, and all Java programmers (young and old) are well versed in the paradigm. Such cannot be said for the C# programmer. C# programmers seem to think that AOP is for testing only. We don't seem to understand the other mega-benefits of AOP. For this reason you should hire Java guys, not C# guys
I've given this political statement consideration over the past several days, I am completely sure that this a further example of the ethnocentric and narrowly learned specialization of all programmers in our field. You see, the Java guy clearly doesn't understand the deep strategy of C#. Neither does he understand the deep strategy of Scala. Neither does the C# programmer understand why AOP is totally essential to life in the Java ecosystem. The C# programmer does not understand why a lack of events and delegates in Java would push the entire ecosystem towards AOP.
Let me tell you about it.
Since the dawn of time, the dream of programmers everywhere has been code reuse and modularity. We want to stop writing the same code over and over again every time we start a project. We want a nice library we can compose with. Composition is a big fucking word in our profession. It is the dream of the blue turtles. We want to write just those little bits of app-specific logic our customers demand. Composition and reuse are the absolute ideas driving most changes over the past 30 years of computer languages.
Simple OOPs is nothing more than an attempt to achieve composition and reuse. In the days of Smalltalk, simple OOPS worked out fairly well. Because of the weak typing system, simple objects did the trick fairly well. It was easy to interchange BaseA object with ModifiedA object, or BaseA with BaseB, or ModifiedA with ModifiedB. The weak and dynamic typing system just went along with it, and if it could work, the interpretor would make it work. For this reason, composition was pretty dang easy in Smalltalk. There was pretty good code reuse in Smalltalk.
The problem is that a lot of us hate interpretors and weak typing systems. A lot of us out-right fear dynamic systems for their unpredictable behavior and their slowness. Others hated the ugly and goofy looking interface of the Smalltalk virtual machine. Ergo, Smalltalk was not widely adoped and it ultimately died. It was always a niche thing which the Ubergeeks used. It is now having an interesting undead afterlife in the form of Squeek, but Smalltalk is dead none the less.
In response to Smalltalk, a number of strongly typed and compiled OOPs languages were invented. C++ and Object Pascal were two perfect examples of this movement. The problem was that they were only half-assed OOPs languages. They were also structured. A lot of Dephi programmers programmed in flat structured Pascal and claimed to be object oriented. Likewise, a lot of C++ programmers were really writing C code and claiming to be object oriented. As you might imagine, this approach did little for Composition and Reuse. Worse still, once the language is strongly typed, interchange of BaseA object with ModifiedA object, or BaseA with BaseB, or ModifiedA with ModifiedB is no longer possible. The signature of a function or a method demands a specific type.
Next, Java came around with a simple language and simple OOPs and the notion that you should inject dependences in an AOP fashion. In this approach, you pass everything by interface type or abstract class. This loosens the type system enough so that interchange of BaseA object with ModifiedA object, or BaseA with BaseB, or ModifiedA with ModifiedB is possible... with in some limits. IoC and DI were formalized in things like Spring, AspectJ, and Guice to make this even easier. You get pretty good code reuse in this AOP approach to doing business. Implicit within this success story is a clear cut admission that pure OOPs doesn't yeild composition and reuse when you are compiled and strongly typed. You need to take an AOP approach, or you don't get composition and reuse at a good high level.
At roughly the same time (a bit earlier) Delphi came around with the notion that you should have something greater than objects called components. These should be wired together with delegates and events. Delphi died. Java won by default. Delphi was reborn as .NET and C#. Suddenly the battle was renewed.
Of all the things that have triggered the greatest number of flame-wars between Java and C# programmers over the past 8 years, a pure lack of understanding of the .NET Component is top of the list. C# programmers know they have components. Some are dimly aware of how they work. Many of them have no ideal in the world that Java lacks components. They don't understand that strategy and command patterns are built into the CLR as first class citizens. Specifically, strategy=events and command=delegates. [Some would rap my knuckles for making that hard fast association. Bring it on. You'll get knocked the fuck out.] These first-class citizens of the .NET framework are the foundation of components and loosely coupled, modular, composable, reusable code framework that we enjoy in the .NET system.
Java programmers do not understand this. Events, delegates, properties, full lexical closures, all these things work together to make AOP far less necessary in the .NET programmer's life than in the Java programmer's life. Coversely, because you lack events, delegates, properties, full lexical closures in Java, you need AOP much more than we do. We get good composition and reuse without AOP. We get good simple code without AOP. If you think your AOP code is clean, you should see our component code. It is wired together and loosely coupled with Events and Delegates.
This is not to say that AOP is completely irrelevant to the C# programmer. Many of us, especially me, are asking serious questions these days like:
1. How can we take advantage of some of the goodness of AOP?
2. When should I select AOP rather than a component approach?
3. What are the specific scenarios where AOP is preferable to writing a component?
4. Can AOP improve certain approaches where components don't work so well?
5. If so how?
So far, there is a lot of debate about this topic among serious minded C# programmers. We don't have a lot of clear-cut, indisputable design-wins and use-cases for AOP. Logging application activies and errors is one winner. Automatic unit testing of production code is another. This is strong sign of the high value of components. They only break down {with certainty} in a couple of scenarios. Of course, we do not yet understand AOP as well as we will in 10 years time. In that time we may have even more compelling use-cases for AOP. Right now, we don't.
It should be noted in passing that several veteran polyglot programmers are rallying against AOP in the .NET world. In particular, the gentileman scholar Ted Faison has written a super book called "Event-Based Programming: Taking Events to the Limit". I remember Ted well from my Delphi days. He was a great Delphi programmer. Like most of us, he moved with Anders to the .NET platform and C#. I like him and respect him, so I read his book.
Although Mr. Faison doesn't say it bluntly, a careful consideration of what he says in this book boils down to the following: "All of you C# programers runing toward AOP are headed in the wrong direction. Don't do AOP just because the Java guys do AOP. Use the events and delegates in our systems to wire together components, and you will obtain better and looser coupling. This will give you the best possible composition and reuse you can obtain today." I reached a conclusion that mega-AOP, as it is practiced in Javaland, is a specific solution pattern, in a specific language, which lacks key attributes we have. Ted Faison might not have actually said that bluntly in his book, but I achieved that realization from reading his book.
This conclusion becomes even more interesting when you consider the design goals of the Scala language. For those who don't know about it, Scala is Java's replacement and successor. This is like Marcus Ulpius Nerva Traianus taking over for Marcus Cocceius Nerva. It is the succession from a good emperor to a better one. Martin Odersky is explicit in his design manifesto for Scala. Two questions drive him:
1. Can we provide better language support for component systems? He wrote a nice paper and did a video about this subject.
2. Can we find a perfect fusion of Object Oriented Programming and Functional Programming at the same time?
Dr. Odersky very delicately offers a tough critique of Java in Scala. You don't actually have a delegate in Scala, but you don't need one. You pass functions without any delegate machinery in any functional language. You don't have interfaces in Scala, but you don't need them. You are better served by Traits, which give you all of the advantages of Interfaces, Mixins, and Multiple inheretence. You don't have to pass things by Trait or by abstract class in Scala. Type Inference and fully-orbed Generics will get the job done for you. This generic based polymorphism obviates the need for OOPs polymorphism. All of this adds up to a system which can do serious components, composition and reuse.
But this Scala approach to the problem is not well understood by Java programmers. This is where they are going to have a big problem adjusting to life in Scala. As a proof consider this: A Java programmer learning Scala posted up at the Scala-Lang.org site. He asked the following question: How do I use Guice in Scala? Since he was a Java programmer who did not want to reinvent the wheel, the question is perfectly understandable. One of the Scala team members on the site answered his question. Paraphrasing, the team member answered that AOP is basically out of place in Scala. You can use it. It is not recommended. It isn't the Scala way of doing things. Scala uses components. Scala achieves composition and reuse by loosely coupling components together. You don't have to inject methods, objects or any dependency anymore. Use a trait, or pass the function or class to the object. The old problems are not a big deal anymore. To get the juice out of Scala, you need to get the Guice out of Scala.
The Java guy seemed extremely disapointed that the Scala team did not appreciate the great value of AOP. It was likewise, I am sure.