On Wed, Feb 7, 2018 at 5:45 PM, roger peppe <rogpe...@gmail.com> wrote:
> As someone totally unfamiliar with the GoF patterns, hre's my take. > I looked at the wikipedia articles and tried to work out what > problem I thought the pattern was trying to address and then > wrote some Go code to do that. I'm generally in agreement > with those who say that these patterns are there largely to avoid > the pitfalls of languages with class-based inheritance. > Some are targeted specifically at C++/Java -- not due to class-based inheritance but to language/syntax limitations. For example the builder pattern is meant to make construction of objects more readable than overloaded constructors/factories with telescoping argument lists. Go has less need for this because of its struct literal syntax. And, for structs that need stronger encapsulation (private fields, enforce invariants at construction time), Go has its own pattern: the functional option pattern <https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis>. They certainly aren't all there to avoid pitfalls of class-based inheritance. Many are intuitive solutions to certain classes of problems: one could easily write code that adheres to one of these patterns without even realizing it (adapters, facades, decorators, interceptors, commands, strategies, flyweights, iterators, observers, DSLs). Admittedly, some are less powerful/expressive without class-based inheritance, so less applicable to Go. But, as alluded to in the previous paragraph, Go has some of its own patterns. > abstract factory https://play.golang.org/p/syHr7QJ9e2q > - anything in Go that returns an interface rather than a concrete type > could be considered an abstract factory. > This isn't quite right. Your example is just a parameterized factory method. This pattern is about the factory itself being abstract. Here's a re-worked example: https://play.golang.org/p/joSYzhMeS0n This pattern is a specialization of the strategy <https://en.wikipedia.org/wiki/Strategy_pattern> pattern. > > facade https://play.golang.org/p/U6DSg5pDC0w > - any type that has another type as a member and not much > significant logic in its own methods could be considered a facade. > And if that facade implements a particular interface then it is also an example of the adapter <https://en.wikipedia.org/wiki/Adapter_pattern> pattern. More adapter examples: bytes.NewReader() and strings.NewReader(). They adapt byte or string to the io.Reader interface. > > proxy https://golang.org/pkg/bufio/#NewReader > - any Go function that takes an interface and returns a > value that implements the same interface could be considered > a proxy. > This describes the decorator pattern more than the proxy pattern. Usually, a proxy isn't just a wrapper (like a decorator is) -- the proxy object is some form of stand-in for some other resource, typically remote. RPC is a much better example of the proxy pattern (like the "net/rpc" package, but also things like gRPC). Sometimes proxies also add functionality (like the decorator pattern), translate protocols (a la the adapter pattern), or perform routing/multiplexing/demultiplexing. Another related pattern is the interceptor pattern (aka chain-of-responsibility <https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern>). > > factory method: https://play.golang.org/p/AmQ7lQHAPDy > - any function or method that creates and returns an object could > be considered a factory. Go doesn't have constructors so this > is just normal. > > This statement describes "factory method", but is not at all what the factory method pattern is about. This pattern is specific to class-based inheritance. The idea is to create a virtual/abstract factory method that sub-classes override to return different sub-types/implementations. Since Go does not have class-based inheritance, the closest I could envision is "override" behavior via a function field. Here's a take on it based on the example in the wikipedia page <https://en.wikipedia.org/wiki/Factory_method_pattern>: https://play.golang.org/p/EnZ4sRFdmyT > visitor: https://play.golang.org/p/w74EhhuAhT5 > - I'm not sure that this is a great pattern. Doing a callback for every > object in a hierarchy seems fine, but requiring a different method > to be implemented for each kind of object seems unnecessary - if > you add another kind of object, then all the code using the visitor > will break (but that could also be considered an advantage, I guess, > as it means your clients are forced to consider all possible kinds). > This one also is more suited to class-based inheritance as there is usually a base class implementation that has a no-op implementation for all callbacks. That way, the code implementing the visitor is not broken whenever a new method is added to the visitor. (In Go, you could embed a no-op visitor, so that you only need to implement the subset of applicable methods.) For what it's worth, adding other kinds of objects is not a common operation. The kinds of objects are usually a closed enumeration -- think productions in a CFG. This is most often used for applying recursive algorithms to complex tree-like structures -- typically ASTs. It can be a nice way to organize that kind of code, but I think the pattern loses a good bit of its expressive power without generics (and also loses a little without traditional class-based polymorphism). So in Go, I think one would just use a normal recursive function with a switch over the kind of input. > On 6 February 2018 at 23:30, <matthewju...@gmail.com> wrote: > >> Your visitor pattern here seems to not be a "visitor" pattern. I would > >> think that the Go equivalent would define an interface, and visit based > on > >> that interface. > > > > > > Here’s what the Wikipedia article says: > > > >> In essence, the visitor allows adding new virtual functions to a family > of > >> classes, without modifying the classes. Instead, a visitor class is > created > >> that implements all of the appropriate specializations of the virtual > >> function. The visitor takes the instance reference as input, and > implements > >> the goal through double dispatch. > > > > > > In my Go conversion the family of classes is the File type. The virtual > > function Accept is added to the family without defining behavior. The > > Dispatcher is interchangeable and implements Accept for each type of > File. > > > > There's a good possibility that I misunderstood the source example, but > I'm > > not sure why this isn't a visitor pattern. > > > >> This isn't really an example of a factory method, because it isn't > >> instantiating things of different types. > > > > > > Vars of func types is like a set of classes with only methods. Lately > I've > > been looking at func types and closures as a pattern that's superior to > > interface in some cases. > > > >> I didn't look at all your examples, but in most cases, it seems like > error > >> handling, which would be an important part of a Go example, is > completely > >> absent in the examples given here. > > > > > > Once I dig out the book I’ll try to put together closer to idiomatic > > examples. > > > > Thanks, > > Matt > > > > On Monday, February 5, 2018 at 11:36:42 AM UTC-6, Eric Johnson wrote: > >> > >> An interesting idea. Some thoughts.... > >> > >> On Friday, February 2, 2018 at 9:03:54 AM UTC-8, matthe...@gmail.com > >> wrote: > >>> > >>> I’m looking at patterns summarized on Wikipedia from “Design Patterns: > >>> Elements of Reusable Object-Oriented Software” and writing out a few > as the > >>> equivalent in Go. > >>> > >>> Visitor: https://play.golang.org/p/A5tNzxMmetH > >> > >> > >> Your visitor pattern here seems to not be a "visitor" pattern. I would > >> think that the Go equivalent would define an interface, and visit based > on > >> that interface. > >> > >>> > >>> > >>> Abstract Factory: https://play.golang.org/p/SWwuX49eysd > >>> > >>> Factory Method: https://play.golang.org/p/FRgDBx2CLFf > >> > >> > >> This isn't really an example of a factory method, because it isn't > >> instantiating things of different types. > >> > >>> > >>> > >>> Facade: https://play.golang.org/p/forPdwy9VCi > >> > >> > >>> > >>> > >>> Proxy: https://play.golang.org/p/DFWuDPTOzEP > >>> > >>> I’m curious how more experienced people rank these and the other > >>> patterns. > >> > >> > >> I didn't look at all your examples, but in most cases, it seems like > error > >> handling, which would be an important part of a Go example, is > completely > >> absent in the examples given here. > >> > >> Eric > >> > >>> > >>> > >>> Matt > > > > -- > > You received this message because you are subscribed to the Google Groups > > "golang-nuts" group. > > To unsubscribe from this group and stop receiving emails from it, send an > > email to golang-nuts+unsubscr...@googlegroups.com. > > For more options, visit https://groups.google.com/d/optout. > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.