4 ms·
I love go, and I concur with almost everything the author wrote here. A small pain point in my experience though is that Go lacks an elegant way to pass type in
by vectorpush 12y ago
I love go, and I concur with almost everything the author wrote here. A small pain point in my experience though is that Go lacks an elegant way to pass type information around without using a zero valued (or fully populated, it makes no difference) instance. For example, I have a factory struct with a New 'method' that returns interface{MySignatures()}, but I cannot inform the factory of the concrete type I want it to produce without passing in something like new(ConcreteStructWithMySignatures). Some have suggested I pass in a string, but what is the point of a type system if I have to use strings to reason about my types?
- melvinmt 12y agoYou're probably using Go in The Wrong Way®. It's hard to fit classical object oriented design patterns onto Go. A factory method that produces structs with dynamic types is an example of that.
- stcredzero 12y agoAgreed. For most of what you'd use a Factory Method for, you can Duck Type your way out of the situation.
- vectorpush 12y agoI created this contrived example to illustrate the basic idea behind the pattern I want to emulate. http://play.golang.org/p/7T2s8Dn0Sr http://play.golang.org/p/7T2s8Dn0Sr Ideally, on line 57 I'd pass in some kind of "first class" type representation that the switch statement could use to branch into the correct type, rather than a zeroed (or not, Go doesn't care) struct or a string. Admittedly, this could definitely be abuse of Go semantics, so I'm also interested in how I could refactor this into a more canonical Go pattern.
- melvinmt 12y agoWell, at line 57 you already know which provider you want to use, so why not just initialize the struct from there and skip the factory method altogether. http://play.golang.org/p/vXJcMJ-j6P http://play.golang.org/p/vXJcMJ-j6P Edit: I think I see what you're trying to do with the factory method. You're trying to enforce the ServiceProvider interface on every provider struct. You don't need to do that yourself, the compiler does that work for you. On line 57, you already know what kind of struct you're dealing with so you can safely call .connect() on the provider. And if you need to pass on the provider to another method, just make sure the method only accepts structs with a ServiceProvider interface. The compiler will stop you when it's not the case.
- vectorpush 12y agoI was in a rush and didn't create the most illustrative example. you already know which provider you want to use, so why not just initialize the struct from there and skip the factory method altogether. The reason that I want ProviderFactory to generate instances is because I also want it to act as a manager for those instances. ProviderFactory can keep track of all ServiceProviders and perform various operations or broker data between instances. Here is my previous example slightly improved. http://play.golang.org/p/XoXu9x9r8f http://play.golang.org/p/XoXu9x9r8f Now, ProviderFactory injects configuration information into ServiceProvider instances based on their concrete type, before establishing a connection. A more complicated app might wrap the NewProvider function into a gorotune/callback that blocks each provider instantiation until a successful connection has been established. This is all beside the point though; all I want is something akin to a special "type" type that the compiler recognizes as a symbol for that type that the factory can use to evaluate which concrete type to produce.
- rakoo 12y agoYou're still leaning too much in types. Go is a very lightweight type system. The setter and base type basically scream for "I want to use my Java in Go". I'm not even talking about using a factory. Here's I would do it: http://play.golang.org/p/EmxCRkDNLC http://play.golang.org/p/EmxCRkDNLC
- NateDad 12y agoYour code seems incredibly backward. Why does the ProviderFactory hold the knowledge about the query interval for each provider? Shouldn't the provider hold that information? With the data structure you have, any time you add a new provider, you'd need to add a new case to that switch statement. That's like the polar opposite of separation of concerns. Let the query interval be defined on the type, not in the factory. The same for the host they connect to. Shouldn't that be defined in the type? Like make WeatherProvider.Connect() call ProviderFactory.WeatherHost() to get the host to connect to. You're doing everything inside out. What do the types even do in your example? This isn't a Go problem, it's programmer problem.
- AnimalMuppet 12y agoSeems to me that if I've got a factory, I need some way to tell it what I want it to produce. That's either a string or an exemplar or some set of properties/flags. Somehow, you have to say what you want. Or have I not understood your pain?
- vectorpush 12y agoRight, and this is what happens in Go. I can use a string or struct as a signal for what type to produce, but I wish I could communicate that type in a "first class" way, rather than using another data type as a proxy signal. Check my reply to melvinmt for a more clear example of what I mean.
- AnimalMuppet 12y agoI'm still unclear on what you mean by "first class". In Java, you could pass in WeatherProvider.class to tell it that you want it to produce a WeatherProvider instance or a string that said "WeatherProvider". Is that the kind of thing you have in mind?
- vectorpush 12y agoI am not a Java programmer but the Java class literal seems like a perfect example. The class literal provides a language mechanism for representing a type of class (in Go's case it would be a type of struct) without the need for an actual instance of the class or another arbitrary data type (like a string or const) that the application is programmed to recognize as a signal for that class.