views:

380

answers:

7

I've been reading through Code Complete, and I just got to the part about Design Patterns. I thought I'd see what questions were popular and tagged design-patterns. I was reading this question, and I agree with what seems to be the consensus there, that Design Patterns exist to address the limits of a paradigm (Functional, Object-Oriented, etc.) or even a language.

I'm a student in a University headed for a Computer Science degree and have been looking for different languages to learn in order to broaden my horizons, and learn something besides OO paradigm languages like Java, C++, etc. (On that list is Haskell, Assembly and/or getting back to C, Factor or Forth, Ocaml, Clojure, Python and/or Ruby, Prolog, Labview, SQL and/or noSQL, and some client-side web stuff like GWT or jquery.) Besides that I thought I could sort through the languages and find one, or a few that I would most like to program in/find a job in and learn those after / while broadening the horizons. (Using criteria in the same vein as these, my list at the moment is Clojure, Ocaml, Scala, Python)

After reading that question I wondered if there was another criterion to compare the power/utility/IWouldReallyPreferToProgramInThisLanguage-ness. If I'm not totally off-base, the smaller the amount of Design Patterns left unsolved or unimplemented by the language or paradigm, the more helpful it would be to programming productivity, readability, expressive power, etc. For example, apparently Scala implements a solution to the Singleton pattern:

In Scala, you have a top level construct called Object in addition to Class. It's lazily instantiated and there is only one. You don't have to use the Singleton pattern to get a Singleton. It's part of the language. --Dan Sickles

or see Ben James' post on the same feature.

I'm not talking strictly about GoF's design patterns here, but anything that can be codified into a useful design pattern.

So I guess I have two major questions.

  1. Does a language or paradigm that requires you to use or implement, on your own, fewer Design Patterns provide noticeable benefits to ease of coding / readability / more or better abstractions / less troublesome to debug / more expressive power / less boilerplate etc.? If so, in which way(s)?

  2. If this does provide benefits, which languages require you to use / implement the fewest design patterns? If not, what are the important things a high level language can do to make using good design practices (like Design patterns) easier? I think that this can be an objective question.



I do realize that this may be difficult to compare because of the difference between the core language and the myriad of libraries that surround it. I would personally eliminate libraries with many conflicting implementations that avoid the same problems / design patterns for 2 reasons. I would think that more than one way to solve the same problem would (a) make it hard for a programmer in a team to understand what is going on when his preferred implementation is not used, and (b) make it more difficult in general to choose the most robust / supported solution among many options.
I guess in order for question two to be objective there would have to be some objective guidelines to decide whether to allow / forbid a library to be counted as solving / implementing a design pattern that the core language does not, but don't punish me for trying. :)

Related questions: Your criteria in using a new technology or programming language

Programming Languages and Design Patterns

Are design patterns really language weaknesses?

Language-integrated design patterns

Are Design patterns specific to Language or Technology?



Final Note: As this is my first question on Stack Overflow, please let me know If I need to change something, or, if you can, feel free to change it for me.

+2  A: 

Does a language or paradigm that requires you to use or implement, on your own, fewer Design Patterns provide noticeable benefits to ease of coding / readability / more or better abstractions / less troublesome to debug / more expressive power / less boilerplate etc.? If so, in which way(s)?

To take a specific example, I think that 'polymorphism' might count as a design pattern.

Some languages (those which support OOP) provide built-in support for this; and other languages (e.g. C and assembly) don't.

In either kind of language (e.g. OOP or procedural) I can implement and use the 'polymorphism' design pattern. The thing is that an OOP language makes it easier: in C I'd be implementing vtables and function pointers; whereas in C++ I am, more simply, declaring inheritance.

Yes, personally I would prefer to work in a high level language with more layers of abstraction. I guess my question is which languages are the best at that, and can we look at design patterns to determine that?

I don't think so. For example, C++ versus JavaScript make it easier or harder to implement some patterns like, I don't know, strong compile-time type-checking versus dynamic binding.

But, IMO, deciding which of these two languages to use for a given purpose is made for other reasons, which are more important than their support for such patterns (for example one tends to be faster and be compilable for any O/S, the other tends to be runnable inside any browser).

Perhaps the most important criteria for choosing a language are:

  1. Do our programmers know this language?
  2. Will our platform run this language?
  3. Will this language interop with other software?
  4. What libraries/frameworks are available for this language?
  5. Does this language have a future (adequate vendor support, preferably multiple-vendor, and suffiently many/cheap programmers)
  6. Does this language allow us to meet our non-functional requirements: speed, security, maintainability, testability, scalability.
  7. Coming in at only about number 7 or so is, is this language more expressive: will it let us do more with less?
ChrisW
I guess polymorphism is not a design pattern by itself. Plus C supports it in a little way (through coercion and overloading, _ad hoc_ polymorphism) :P
rano
@rano I think that a "design pattern" is no more or less than something that's described using a [Pattern Language](http://en.wikipedia.org/wiki/Pattern_language) (e.g. "name"; "problem/forces"; "solution"; "see also"), which polymorphism certainly could be.
ChrisW
Nope, first of all polymorphism is too generic since it describes a family of programming language properties (which one? parametric, by inclusion, overloading, etc), secondly you can create a pattern that _uses_ polymorphism in a particular way to solve a problem/force and name it, but polymorphism itself is not a pattern
rano
rano
@rano Part of the problem with the premise of the OP, that you can tell how powerful a language by counting how many design patterns it supports, is that there's no especially good way to list design patterns.
ChrisW
That is true but also you cannot say 'something is a design pattern' since it can be used in/facilitates some patterns
rano
@ChrisW I realize that, but should there be a better way to list them? Would it make a significant difference if we did? Maybe not.Actually, I was a little pessimistic and asked to find the one that failed to support/need the fewest.
Trevor
@Trevor "should there be a better way to list them?" - As programmers we do a lot of taxonomy of functionality when we write software; but, and I'm not sure why, I'm not so interested in the taxonomy of knowledge (that's, as it were, for librarians not programmers): perhaps because related ideas overlap so much.
ChrisW
@Trevor Anyway, the truths are that 1) Design patterns describe software design elements, and 2) Some design patterns are easier to implement in some languages than others. I'm not sure that trying to count [all] design patterns is helpful; but it's interesting to compare languages' support for specific design patterns.
ChrisW
+1  A: 

I think comparing languages based on the implementations of patterns might lead to wrong conclusions.

The way I see patterns that they are solutions, which have worked for many people in many circumstances, to certain problems. I interprete problem in this context as a set of forces pulling in different directions making it relatively hard to choose a good approach.

The reason I see a potential to misdirection is that although the problem domain causes the forces, the inplementation language creates a lot of forces due to its paradigms, limitations, supporting libraries etc.

An 'Interceptor' pattern will probably never be invented in the Ruby community because their paradigm includes metaprogramming which makes this dissappear. They would probably never face a forcefield leading them to invent that pattern.

In this context, patterns become 'Solutions looking for a problem'.

So I think rating programming languages based on how well they solve problems which almost never happen in that language will be of limited value.

Peter Tillemans
+9  A: 

First of all:

Design patterns are not language weaknesses, but just a standardized term for certain, frequent constructs such that they can be directly recognized and understood by programmers. Design patterns are above languages, beyond them.

Take the strategy pattern. What does it basically mean?

the strategy pattern [...] is a particular software design pattern, whereby algorithms can be selected at runtime.

No more, no less. Now if the programmer wishes to express this kind of pattern, he has to implement it - and for the implementation, he's of course limited to the language's capabilities.

One might call it a weakness of Java/C# what we've got to write a weird

interface IComparison {
   int Compare(Foo a, Foo b);
}

void Sort(Foo[] data, IComparison comp);

where a functional language could use higher-order functions to just have the whole signature as

sort :: (a -> a -> Ordering) -> [a] -> [a] 

but it both implements the same pattern, just with more or less powerful implementations.

So:

Does a language or paradigm that requires you to use or implement, on your own, fewer Design Patterns provide noticeable benefits to ease of coding / readability / more or better abstractions / less troublesome to debug / more expressive power / less boilerplate etc.? If so, in which way(s)?

In this context, functional programming is worth being mentioned. Statically typed functional programming (as in *ML, Haskell, F#) is known for being

  • extremely type-safe
  • expressive & readable
  • easy to debug (small composable functions, no unintended mutations)
  • often heavily generic by default (type-inference)

From a design-pattern point of view, many patterns are that easily expressible in a functional way they almost seem built-in, as well as making other, change-based patterns completely unnecessary.

I) Strategy, Template method

It's just higher-order functions. Just work with functions as if they were values

sort (compare `on` length) ["Hello", ", ", "World"]

II) Visitor, Visitor

Algebraic datatypes + pattern matching make an explicit visitor implementation unnecessary.

data Tree a = Leaf | Node (Tree a) a (Tree a)

height Leaf = 0
height (Node left _ right) = 1 + max (height left) (height right) 

III) Null object

Covered by the type system or completely unnecessary. Use the type-safe Maybe a ADT plus pattern matching, if it really should be needed. This is rare since data don't change and are therefore always initialized.

IV) Adapter, Iterator, Composite

Are usually covered by the more powerful type-system (type-classes) plus advanced control structures.

V) Listener/Observer

Greatly covered by reactive programming approaches like F#'s synthetic events or higher-order observables.

VI) Singleton

Why? Apart from having left OO paradigm, there's no need anyway. Shared data are immutable and cannot be changed. How many "instances" are produced does therefore not matter.

VII) Inversion of control

Monads cover them all ;) You can arbitrarily change the flow of control while still allowing to write code in a completely straightforward way. Just take a look at async workflows:

let AsyncHttp(url:string) =
    async {  // Create the web request object
             let req = WebRequest.Create(url)                
             // Get the response, asynchronously
             let! rsp = req.GetResponseAsync()
             // Grab the response stream and a reader. Clean up when we're done
             use stream = rsp.GetResponseStream()
             use reader = new System.IO.StreamReader(stream)
             // synchronous read-to-end
             return reader.ReadToEnd() }

This function is actually divided into two parts at the let! which get called when data arrive. Nevertheless, there is no begin/end-pattern or any variation from a usual, sequential style at all, which would be required in e.g. C#.

Dario
btw, C# allows more Haskell-style (functional, not oop), take a look here: http://msdn.microsoft.com/en-us/library/bb534966.aspx especially at samples.
Andrey
@Andrey: Yes, C#, as many modern OO languages, is in a process of merging in many functional features. That's where all the lambas, type inferences and immutable datastructures come from. Even linq is nothing but a fully working monad syntax: http://stackoverflow.com/questions/1418106
Dario
@Dario i know, but you put C# in same category with Java, but C# is ahead of it with functional features.
Andrey
@Andrey: True. What's why I recommend Scala for targeting JVM ;)
Dario
A: 

To answer your first question with an example: events and listeners/handlers in Java vs. C#.

From what I remember of coding in Java, to create an eventing model, I'd have to create:

  • a listener interface
  • implementation of the listener interface in the listener classes
  • methods to register listeners in the class that will raise the event
  • code to invoke all the registered listeners

In C# you only have to:

  • declare the event, along with the required method signature for event handlers

Registration and invocation of event handlers has all been provided by C#. Also, you don't have to define an interface for the listener; as long as the method signature matches, you can use the method as an event handler.

In short, C#'s event model is hidden from the developer's view, unlike Java's. So C# has less boilerplate code, better readability and it's easier to code.

On the other hand, events in C# also use 'magic'; the logic that makes it work is hidden from the developers view. This can make it a source of bugs if that magic isn't understood by the developer.

Niels van der Rest
+1  A: 

I would argue that the very concept of a language with "fewer design patterns" than another one, let alone a value judgment based on such a metric, is nonsensical.

A design pattern, fundamentally, describes "a problem which occurs over and over again," and "the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice," to quote Christopher Alexander. He's talking about building and architecture, of course, not software, but I think he captures what is essential about design patterns in both fields. So long as a particular domain has problems to solve, and software engineering produces a solution to that problem that is expressible in terms of a particular language, that language has design patterns. Design patterns are as endless and varied as the problems that they address. Attempting to enumerate them is a fool's errand (though we are occasionally happy to be fools, if it means we'll learn something along the way :-) ).

For example, use of a functional programming language may not exhibit exactly the same set of design patterns catalogued in GoF, but for every pattern you don't see there is a functional programming pattern that is awkward or inexpressible in another language. Many of these patterns have yet to be named. My suspicion is that this is perhaps because functional programmers do not live in the Kingdom of Nouns quite as much as object-oriented programmers do.

There are two kinds of limitation at work in design patterns, neither of which is necessarily a bad thing:

  1. The language imposes a structure that design patterns are molded around, just as design patterns in architecture are determined by the environment and materials at hand. This must be so, or the solution would not be realizable! Design patterns can illustrate particularly elegant solutions a language provides for certain problems, just as they can be well-established idioms that work around limitations of the language.
  2. The formal documentation of a "design pattern" can sometimes blind us to all the different variations of a particular solution. Worse, the application of design patterns can actually lead us astray from good design! When you've seen code where an engineer appears to have indiscriminately shaken their GoF book over the keyboard to let the patterns fall wherever they may, you'll realize that design patterns are sometimes solutions in search of problems. Even Alexander warns about getting too worked up about the systematic application of design patterns – they are to him a crutch, to be laid aside once intuition and good design are ingrained in your practice.

So yes, there's limitations inherent in design patterns, but those limitations do not necessarily have negative connotations, or have anything to do with the language per se.

What I think would be more a more fruitful direction for research is a comparison of why particular design patterns do and do not exist in practice in different programming languages, and what this says about those languages.

Owen S.
A: 
delnan
"Would you want to learn a language which has a special case built in for every possible problem 5 or more people might encounter?"In short, no. But I would think that if a majority of savvy programmers agree it's a useful fairly common pattern, it would be better for everyone to have a well-tested way to solve that problem, if that's possible (depending on how we define 'design pattern') and include it in the core language, or at least the most popular external library for the language. Perhaps that's not really practical, but I still have a few years to be an idealist, right?
Trevor
@Trevor: I absolutely agree that a common problem should be already solved by the language, the standard library or a de-facto standard library. But I disagree that the *language* should therefore have many design patterns built-in. It should instead be powerful enough that many other problems can be solved in a library *without (much) boilerplate code* in the "client code". Keeps the language relatively simple, but still solves the problem.
delnan
+2  A: 

I think you're getting way too theoretical. Once you get out of school, the best languages are the ones that get the job done in the context of the team you're on.

How can you even count design patterns really? Much less count which are provided natively by a language... Its like counting how many angels can dance on the head of a pin.

If you're interested in language design, and you're interested in design patterns, you might enjoy the book "DSLs in Boo" by Ayende Rahien. It shows patterns for implementing languages specific to a given business problem. Great stuff. Ayende ships a lot of software so it's practical too.

Frank Schwieterman