4 ms·
This is tough to answer without a language in mind. Depending on what access you have you can use interfaces to get run time method binding or composition to ge
by cy_hauser 6y ago
This is tough to answer without a language in mind. Depending on what access you have you can use interfaces to get run time method binding or composition to get compile time binding. For either of these you'll have to "wire" the needed types together. This is what Go has you doing.
Going back to the "old days" you'd reach for a pattern. Adapter pattern was the most common. Bridge or Facade maybe? But patterns fell out of favor a while back (imo) because they were tough on the coder's mind. A lot of languages added useful bits to create these pattern behaviors more naturally and without reaching for a book. I'd need to see code to be more specific.
- hnick 6y agoI'm not familiar with Go but apparently it has "embedding" which is a little different. Maybe that works. But it's largely playing the same role - a way to extend a class you might not control. So I'd rephrase that I suppose, perhaps we don't need something called Inheritance, but we do need a way to extend objects we don't control without breaking encapsulation. As an example if you had a method that outputs XML and you want JSON, you can use an adapter object to call it, read the string, and reformat and pass it through. That works (and I've done things just as bad or worse many times from necessity), but it's definitely more hackish and less efficient than a solution that uses object fields to output JSON natively. Inheritance is my familiar way to do this in cases where I don't control the code (in which case injecting a Writer class of some sort probably makes more sense).
- cy_hauser 6y agoGo's embedding _is_ composition (has-a) in any other OO language but with a little sugar. Embedding allows the compiler to optimize the memory layout for a composed object and allows you to reference the composed object's fields without a qualifier. For example, if a Student class has-a Address {city, state, zip} then you could refer to the city field by coding aStudent.city rather than aStudent.address.city. It's handy but doesn't otherwise change "normal" composition. I'm not advocating against inheritance. If it works then go for it. I do when I'm coding in languages that have it. This stemmed from a thread discussing whether the lack of inheritance in Go was sufficient to exclude it from being an object-oriented language.
- throwaway894345 6y ago> but we do need a way to extend objects we don't control without breaking encapsulation. By definition, you can't extend objects you don't control without violating encapsulation. Encapsulation is all about controlling access so that the owner of a class can make changes without breaking downstream code. If downstream code accesses these private APIs, the downstream code may break, regardless of whether the access was via inheritance or composition (as a side note, "protected" makes no sense--who cares if downstream code breaks because it was accessed by inheritance but not by composition?). > As an example if you had a method that outputs XML and you want JSON, you can use an adapter object to call it, read the string, and reformat and pass it through. That works (and I've done things just as bad or worse many times from necessity), but it's definitely more hackish and less efficient than a solution that uses object fields to output JSON natively. Inheritance is my familiar way to do this in cases where I don't control the code (in which case injecting a Writer class of some sort probably makes more sense). You can do this just as easily without inheritance. You just need a way to access the member fields--whether you access them via inheritance or otherwise doesn't really matter. E.g., class Foo: def __init__(self, x: int, y: int) -> None: self.x = x self.y = y def xml(self) -> str: return f"<foo x={self.x} y={self.y} />" # Via inheritance class JSONFoo(Foo): def json(self) -> str: return json.dumps({"x": self.x, "y": self.y}) # Via composition class JSONFoo: def __init__(self, foo: Foo) -> None: self.foo = foo def xml(self) -> str: return self.foo.xml() def json(self) -> str: return json.dumps({"x": self.foo.x, "y": self.foo.y}) # Simple def json_foo(foo: Foo) -> str: return json.dumps({"x": foo.x, "y": foo.y}) > I'm not familiar with Go but apparently it has "embedding" which is a little different. Maybe that works. But it's largely playing the same role - a way to extend a class you might not control. Go's embedding is exactly composition. It doesn't let you access private member data of the embedded class. It's just syntax sugar for delegation. In other words, you could create the composition version of the JSONFoo class like this: type Foo struct { X: int; Y: int } func (f *Foo) XML() string { return fmt.Sprintf("<foo x=%d y=%d />", f.X, f.Y) } // The compiler will automatically generate this method: // // func (jf *JSONFoo) XML() string { return jf.Foo.XML() } // // And for a given instance of JSONFoo, the compiler will convert all instances // of jsonFooInstance.X to jsonFooInstance.Foo.X. type JSONFoo struct {Foo} func (jf *JSONFoo) JSON() string { return fmt.Sprintf(`{"x": %d, "y": %d}`, jf.X, jf.Y) } Note that a `JSONFoo` isn't a `Foo`. This is an error: func printXML(foo *Foo) { fmt.Println(foo.XML()) } func main() { jsonFooInstance := JSONFoo{0, 0} // printXML(jsonFooInstance) // error printXML(jsonFooInstance.Foo) // correct! }