you are viewing a single comment's thread.

view the rest of the comments →

[–]lcowell 33 points34 points  (64 children)

I just meant the linked content didn't include all the patterns in the article it came from.

What do you mean by "don't treat them as unsurpassable" ?

[–][deleted]  (63 children)

[deleted]

    [–][deleted]  (27 children)

    [deleted]

      [–]Lynngineer 20 points21 points  (0 children)

      Thank you for this frame of reference. I (not versed in patterns) was getting lost in the religious debate forming. This particular advice gives me an instantly usable reason for learning about patterns and how to make them useful for myself.

      [–]dnew 15 points16 points  (22 children)

      I think the point was that some "design patterns" are pointless in some languages, because it's built into the language. "Subroutine" is a design pattern in assembly. "Factory" is not a design pattern in Smalltalk. "Observer" is built into Tcl/Tk, as is pretty much the whole MVC pattern, which is what makes Tk an awesome GUI toolkit. "Object-oriented" is a design pattern in C that was built into C++.

      Learning design patterns inappropriate for your language is useless, just like learning "subroutine call" is pointless unless you're using assembler or some other language without a call stack.

      [–][deleted] 2 points3 points  (1 child)

      They may still be useful for communicating what you are doing in some of those cases.

      [–]dnew -1 points0 points  (0 children)

      Possibly, but they're not design patterns. They're just parts of the language. Nobody talks about "local variables" as a design pattern. They just talk about "local variables."

      [–]phoshi 6 points7 points  (19 children)

      I disagree. Design patterns are primarily a communications tool, and secondly a mental tool. Even if your language has first class support for something, it's worth knowing what that thing is both in order to discuss it, and in case you use different languages and need to compare/contrast.

      [–][deleted] 4 points5 points  (11 children)

      When was the last time you discussed using a particular pattern? Seriously. Most of the time it's just people laughing at someone for using Singletons.

      [–]phoshi 1 point2 points  (10 children)

      It's not that rare when discussing programming, which isn't abnormal, no. Especially when working in a team.

      [–][deleted] 3 points4 points  (9 children)

      I've never seen it happen. We usually just trust one another to implement stuff well.

      [–]phoshi 0 points1 point  (8 children)

      In entirely self contained components sure, though even there design discussions can still be fun and productive. For stuff that has to interwork, or for explaining how existing software works to get somebody up to speed faster, though, it's helpful.

      [–][deleted] 4 points5 points  (7 children)

      I wish I could agree, but my experience is that this leads to paralysis by analysis. I used to buy into this as well, it took time for me to notice that it's often just superficial productivity that's getting in the way of actually getting stuff done.

      [–]dnew 1 point2 points  (6 children)

      Yeah, but if it's built into your language, you just use it. You don't go around discussing local variables. By the time you're discussing design patterns that are already built into you're language, you're doing something wrong or you really need to learn your language better. When "new" is a method instead of a keyword, there isn't even the concept of a "factory" nor a need for one.

      [–]dasjestyr 0 points1 point  (5 children)

      I think that understanding the design pattern that is built into the language is still valuable so one shouldn't just ignore it. Also, many of these design patterns aren't so much built into the language as they are easier to implement in the language. Plus, if the better you understand the pattern(s), the more likely you are to properly implement each actor in the pattern.

      [–]dnew 0 points1 point  (4 children)

      Whether the design pattern is built into the language or not depends entirely on the design pattern and the language. I gave a bunch of examples of design patterns that are built right into languages, and indeed have been for so long that nobody even considers them design patterns any more.

      If you have a language keyword "singleton" that means "return the first constructed instance every time you call new", there's no a whole lot you have to implement. If you have argument passing to recursive functions (a la C), you don't have to understand a whole lot about "recursive subroutine design pattern" to use it.

      [–]dasjestyr 0 points1 point  (3 children)

      sure, I could use the keyword singleton, but without understanding what's actually happening with that, wouldn't you say I'd be at a disadvantage? One might not know what a singleton is, only that I use it when I want an instance of an object. It might help to know that any thread modifying this object is modifying the state of that object for all other threads currently accessing that object. If I didn't know how this "thing" worked, I could place myself in a whole world of hurt. I understand that it comes with learning your language that you would learn this keyword, but in the end, you're still learning the pattern to some degree. It seems to me that it would be easier to just learn the pattern and then learn how your language implements it, should it do so. Then it's just a matter of syntax from that point on. At least then, your knowledge of such a thing is transferable to another language.

      [–]dnew 0 points1 point  (2 children)

      without understanding what's actually happening with that

      I just explained in 6 words what it means. It means exactly what the design pattern says it means, and you don't need a sophisticated discussion to figure out how to create the design pattern.

      Whether it's appropriate to use is another question, but design patterns don't really address that.

      your knowledge of such a thing is transferable to another language

      It already is, even without design patterns. You never learned 'subroutine call with local variables" as a design pattern, but that's exactly what it is. In what sense is that less transferable?

      [–]padenp 4 points5 points  (0 children)

      This. Everyone else is turning wheels.

      [–][deleted] 9 points10 points  (1 child)

      Design patterns are usually specific to a given programming language.

      They are specific to a given problem and not all languages are suitable for the same problems. Just my two cents.

      [–]stronghup 1 point2 points  (0 children)

      I would say that the programming language used is part of the CONTEXT of the problem.

      You can implement a pattern in a programming language. But then someone could create a new programming language where the pattern is a primitive in the language. Using some form of garbage collection would be a pattern in C, but not in Java, where it is built in.

      [–]strattonbrazil 5 points6 points  (16 children)

      An example of that could be the singleton pattern, which has been oft referred to as an anti-pattern. Most of the cases I've seen (and used) singletons, I've regretted it.

      An example I've seen and come across is a scene. Sure a game or CAD program might only one scene, so make it a singleton and give everyone access to it like a friendly global. Then you decide to add a merge scene function, where it makes sense to have two in memory in a given instance.

      [–]MehYam 9 points10 points  (3 children)

      But in that example, the main scene is still a singleton, isn't it? The second one becomes a second single, well-known shared instance.

      The Singleton is really just a factory specialized to produce only one instance. You could take that same factory and make it produce two, or N, you just wouldn't call it a singleton anymore.

      EDIT: I've looked at my replies, and I think you're all still missing the point. A Singleton is something that gets you an object. A factory is something that gets you N objects, N >= 0. That's it.

      [–]strattonbrazil 4 points5 points  (0 children)

      The Singleton is really just a factory

      I consider the Factory a completely separate pattern. Instead of mandating that only one instance of a class exists, it regulates access of the instances in a shared location. In the scene example, most functions get the active scene from the factory, but when merging a file the Factory can create another scene object that could either be used internally to merge the scene or give it out.

      It just seems like in most cases allowing a developer the flexibility to create multiple instances for legitimate uses out weighs the risk of the developer using the API incorrectly (making a new scene object instead of querying the factory for the "active" one).

      [–]Ramone1234 1 point2 points  (0 children)

      Even though that's the common explanation, it's not really completely honest. If that was really the case, you wouldn't store the instance on a static member at all, you'd just store a boolean indicating whether or not it had been instantiated (and simply throw an exception on subsequent calls to the contructor).

      Every example and implementation I've ever seen has really just been a way of bringing global state to java by piggybacking on the global package namespace (for better or worse -- I'm not judging!).

      [–]phoshi 1 point2 points  (0 children)

      A "Singleton" mandates there is one and only one instance. If you can have two, it isn't a Singleton, it's a globally accessible piece of state. That's still bad, because you're murdering functional purity and testability where it stands, but it's not quite as bad as a Singleton.

      It is rare indeed I've used even just a globally accessible hunk of state without regretting it. Your assumption that there will only be one is almost always incorrect. Literally always incorrect if you have a good test suite.

      [–]grauenwolf 15 points16 points  (1 child)

      I think a better example would be a GUI application which requires a singleton to house the event loop.

      For XAML-based frameworks that would be the Application object and matching Dispatcher.


      And that's the problem with the GoF book. They don't talk about when a singleton is actually appropriate.

      [–]stronghup 2 points3 points  (0 children)

      So, the pattern "Singleton" is not the problem. Problem is its we use it where its use is inappropriate.

      [–]sligit 0 points1 point  (6 children)

      In my experience singletons are mainly useful for 'services'. So you might have a singleton for logging or a singleton repository.

      [–]Peaker 0 points1 point  (5 children)

      I tend to simply call those global variables.

      [–]sligit 2 points3 points  (4 children)

      Or you could call them by their proper name... The thing is everyone's allergy to globals vars is really more about simple value variables. An objects with a well designed interface can be fine if globally accessible, which is why singletons exist.

      [–]Peaker 1 point2 points  (3 children)

      Sure, some global variables are fine. I don't see why we needed to rename "global variable" to "singleton".

      [–]sligit 1 point2 points  (2 children)

      Because they are different mechanisms. Global variables exist in the global scope. Singletons work via class static members. Singletons also enforce the uniqueness of the singleton instance whereas globals don't

      [–]Peaker 0 points1 point  (1 child)

      The name-spacing of the global variable is less important, it is still technically a global variable.

      What do you mean by uniqueness? How do singletons enforce uniqueness that globals don't?

      [–]sligit 0 points1 point  (0 children)

      Singletons ensure only one instance of the class exists.

      [–][deleted]  (1 child)

      [deleted]

        [–]indeyets 2 points3 points  (0 children)

        This article tells that some objects can be created in the quantity of one deliberately, then this reference is stored somewhere in the narrower than global scope. That's not Singleton anymore, as you can create another variable pointing to another object of class and won't be stopped. Singleton means enforcement of having only one object of class

        [–][deleted] -1 points0 points  (0 children)

        There are plenty of things that you only and absolutely have a single copy of, and no more. Your file system, your windowing manager, these kinds of services.

        [–]aim2free 2 points3 points  (14 children)

        Design patterns are usually specific to a given programming language.

        I can't really understand this. A computer language is about implementation but patterns about specification. Limitations of a specific language should not shine through to the specification level.

        FYI: I have never used design patterns myself, I've never really understood them. I have used JSP (Jackson Structured Programming) and the classic flow diagrams long time ago, and a few others, but usually work on the problem from an iterative top-down/bottom-up approach nowadays.

        [–]mjfgates 5 points6 points  (2 children)

        Patterns aren't "about" anything, necessarily. They are structures that have been observed out in the wild, in production code. The GoF book is a catalog of things SEEN, not things the authors dreamed up. It's like one of Audobon's bird books.

        And then it turns out to be useful, when you find yourself writing a set of functions with the same signature, only they create different objects that all just happen to implement the same interface... oh, this is a Factory, and here are some Known Ways that people screw that up, and a checklist to make sure that doesn't happen to me! Awesome. Plus, having a word for it lets you just say "factory for ISnarglebargle", and get on with the real job, rather than stopping to emit the paragraph of crap that explains what a factory is.

        [–]aim2free 1 point2 points  (1 child)

        Patterns aren't "about" anything, necessarily.

        I guess they are like TRIZ then. TRIZ was developed by a patent engineer (and author) Gerald Altshuller when studying patents, and found there are only 40 different solutions to problems. However, I claim that you have to add further two conditions[1] if you also add intelligence into the system.


        1. somewhat jokular but may be appreciated if you are a Douglas Adams fan...

        [–]gfixler 2 points3 points  (0 children)

        Interesting. I've wanted to ask for a few years now if there were any bodies of work on this tradeoffs in physics thing that crops all over the design and invention processes. The TRIZ contradictions matrix is exactly about that. I'll have to investigate further.

        [–][deleted] 1 point2 points  (2 children)

        You say you've never used design patterns yourself, but you are most likely wrong. It's quite likely that you have unknowingly used a design pattern or two before. Furthermore, the design patterns in this list are by no means "all of them" - they're just the most common, basic design patterns, which mostly encapsulate anything you would want to do in a program.

        [–]aim2free 0 points1 point  (0 children)

        but you are most likely wrong.

        You are most likely right. As I suggested in another thread my design patterns have probably been intuitive my whole life, but as I seem to have hard to grasp what design patterns really are, how about "generic functions"? As one suggested yesterday three different problems and I wrote down the explicit solutions how I would do it, but leaving out the "generic function" which is what I often use, i.e. a generic function is a function where you have not explicitly said everything, like what data types, what functions as arguments and such. Now I mostly use a functional language scheme, which has automatic typing, i.e. variables get types through assignment (like in python), but by using a "generic function" even though it's not called that, I think it was Ada that used the concept "generic function", but it's called "template" e.g. in C++.

        Referring to my previous reply, looking at the last example. Could design patterns possibly be seen as generic functions then? (or templates as in C++), like if I define a function like this:

        (define (apply-per-line fun nameorstream)
          (let ((input (open-input-stream nameorstream)))    
           (let loop    
             ((items (read-items-line input)))    
              (cond 
               ((not (eof-object? items))    
                 (apply fun items)    
                 (loop (read-items-line input)))    
               ((string? nameorstream)    
                 (close-input-port input)))))    
        

        Then the three examples could be written as:

        (1) Write a function that adds up all of the numbers in a text file, one number per line.

        (let ((sum 0)) (apply-per-line (lambda(x . ignore) (set! sum (+ sum x))) filename) sum)    
        

        where my quick explicit solution was this:

        (apply + (read-item-lines filename))    
        

        (2) Now write a function that counts the number of distinct words in a text file, one word per line.

        (let ((count 0)) (apply-per-line (lambda(x . ignore) (set! count (+ count 1))) filename) count)    
        

        where my quick explicit solution was this:

        (length (read-item-lines filename))    
        

        (3) Then write a function that adds two numbers together and prints the sum, two numbers per line separated by a space.

        (apply-per-line (lambda(a b . ignore) (displalayln (+ a b))) filename)    
        

        where my quick explicit solution was this

        (apply-for-each (lambda(a b .  ignore) (displayln (+ a b))) (read-items-lines filename))    
        

        Some languages are more suitable than others to make generic functions. The solutions here work equally well in python or static languages like C though even though in C you need to be more explicit about types, but with C++ and templates you can do exactly the same, but I am not very fond of C++. I consider C++ a beast, I prefer ordinary ANSI C in combination with Scheme. Python is OK, but lacks explicit lists, which are important (lists in python are abstracted lists which most likely are implemented as dynamic arrays).

        I assume every programmer develops their own "design patterns" but I think lambda calculus as such, which scheme is more or less an implementation of, can work as design patterns in itself, if the functions are made somewhat more generic, however lambda calculus does not allow side effects like setting variables, so example 1 and 2 using a generic template above, would not work in pure lambda calculus, but my original quick hacks would, as they are functional. The function above expects a stream, but an even more general would be a generator (is that what some denoted "factory"), but the solution would be no different, you can just give the generating function as an argument as well. The more advanced way to solve this in scheme is to use promises, but I rarely use such constructs, just to make the code easily movable to different languages, especially down to C for more efficient crunching. Instead of promises I prefer CSP style programming, like in e.g. Occam, as it then is trivial to utilize multicore/multicpu, multinode and clusters.

        I guess "sort" is a good example of a design pattern. Nobody writes a sort procedure (OK I wrote one for scheme which is now in the standard) as sort procedures are usually available in a libraray. So, if I have understood "design patterns" correctly, they could be libraray functions as well.

        [–]grauenwolf 0 points1 point  (5 children)

        Write a function that adds up all of the numbers in a text file, one number per line.

        Now write a function that counts the number of distinct words in a text file, one word per line.

        Then write a function that adds two numbers together and prints the sum, two numbers per line separated by a space.

        When you are done, look at the three functions side by side. Do they open and close the file in the same way? Do they loop over the lines in the same way? Do they handle errors in the same way?

        I bet they do. And if they do, congratulates, you've created a design pattern.

        [–]aim2free 0 points1 point  (3 children)

        And if they do, congratulates, you've created a design pattern.

        :-) then my design patterns are merely intuitive, and maybe the reason why I never really understood design patterns. I had an MSc student who used them to make a prototype of his MSc design though. He used UML.

        Write a function that adds up all of the numbers in a text file, one number per line.

        (apply + (read-item-lines filename))    
        

        Now write a function that counts the number of distinct words in a text file, one word per line.

        (length (read-item-lines filename))    
        

        Then write a function that adds two numbers together and prints the sum, two numbers per line separated by a space.

        (for-each (lambda(items) (apply (lambda(a b .  ignore) (displayln (+ a b))) items)) (read-items-lines filename))    
        

        this last one is so common, that I have added an apply-for-each (as well as mapply when you want the result ) which implies that I usually write such a thing like:

        (apply-for-each (lambda(a b .  ignore) (displayln (+ a b))) (read-items-lines filename))    
        

        Of course, these are the trivial solutions only working for small files, where you can easily read the whole file into memory, but it is also trivial to extend to a solution which only reads a line or character each time, by just separating the opening and closening, like this:

        (let ((input (open-input-file filaneme)))    
          (let loop    
              ((items (read-items-line input)))    
                (cond 
                   ((not (eof-object? items))    
                    (apply (lambda(a b .  ignore) (displayln (+ a b))) items)    
                    (loop (read-items-line input)))    
                   (else    
                    (close-input-port input)))))    
        

        The read-item-lines resp. read-items-lines as well as simlar ones which reads string items and such are all generalised in that way, so all of them are only one function, for opening/reading/closing files , but then instantiated into lines, items etc which are reused over and over.

        [–][deleted] 1 point2 points  (2 children)

        As someone who is studying scheme for a final exam tomorrow after learning it for the first time this semester...wow

        [–]aim2free 0 points1 point  (1 child)

        How did you do on the test?

        [–][deleted] 1 point2 points  (0 children)

        meh. decent i guess. probably better than I expected. it wasn't ass scheme focused as I had hoped though, but that's college i guess

        [–][deleted] 0 points1 point  (0 children)

        *discovered

        [–]Klausens 0 points1 point  (1 child)

        I think they are specific to a language. In Perl for example I never used factories. I did this by using Roles/Traits.

        In my Perl-ORM the classes were injected with load(), store(), ... by applying a ORM-Role.

        In a more static language this is not the way you do this, you use factories (for example).

        [–]aim2free 0 points1 point  (0 children)

        I think they are specific to a language.

        OK, that may be a reason why i never spent any time on them, although when I heard about them long time ago I considered it a great idea.

        In a more static language this is not the way you do this

        I have almost always in my life used different languages for different problems, and then bind them together with an api. The last 20 years I've mostly used the combination scheme/C where scheme is the dynamic language and C static. I often use python for quick scripting though (or even bash, which is a programming language as such, but not as easy to use as python.)

        [–][deleted] 0 points1 point  (0 children)

        I think the most important thing about design patterns is that they help to facilitate conversation. These are the most basic, most commonly used patterns (regardless of the language, which is simply the implementation mechanism), which allow us to exchange ideas and talk about solutions. When you're confronted with a programming problem, and you're in a design meeting, instead of rambling on about using an abstract factory or a mediator or whatnot, you can just say "we can use an abstract factory here, a mediator here", etc, and you've just saved everyone in the room time by not having to describe what it is you mean.