digitalmars.D - Partial class implementation
- Robert Fraser (3/3) Jul 16 2007 Random, C#-inspired thought:
- Tristam MacDonald (3/8) Jul 16 2007 This might be an interesting perspective on the kind of harm such an opt...
- janderson (10/21) Jul 16 2007 I thought it was possible to do something like:
- Robert Fraser (7/18) Jul 16 2007 That article is indeed well-reasoned, however it fails to take into acco...
- janderson (31/76) Jul 16 2007 Classes should be small and as minimal as possible. I think this is
- janderson (42/42) Jul 16 2007 I want to extend what I was saying about components.
- Bill Baxter (8/13) Jul 16 2007 Does that really hold true for D though? Without Koenig lookup, free
- Reiner Pope (32/48) Jul 17 2007 I didn't realise there was an issue here, before; can you tell me if
- Bill Baxter (32/92) Jul 17 2007 I think that's ok, insofar as what you've written. What you can't do is...
- BCS (12/15) Jul 17 2007 IIRC this works. Or did I misread you?
- Robert Fraser (2/24) Jul 17 2007 Yup; that's what I'm doing now.
- Robert Fraser (61/108) Jul 17 2007 I disagree here, because you are making the assumption that there will n...
- James Dennett (31/80) Jul 17 2007 No such assumption is present. In fact, quite the reverse;
- janderson (2/93) Jul 19 2007 I couldn't have put it better. Thanks James.
- janderson (78/155) Jul 18 2007 never be a need to refactor (with D's property syntax, this is somewhat
- Robert Fraser (7/23) Jul 19 2007 Indeed, my company style guide requires that getters/setters exist for a...
- janderson (8/13) Jul 20 2007 It depends where your interviewing, the style the company uses. They
- janderson (13/17) Jul 20 2007 Personally I hate writing tons of getter setters. Its like smashing my
- Robert Fraser (3/23) Jul 20 2007 I agree, but with a good IDE, this is a non-issue. I refer to the fields...
- Robert Fraser (2/6) Jul 18 2007 Indeed, for the exceptional (mostly-data) types, working with external f...
- Robert Fraser (2/10) Jul 18 2007
It would be nice if it were possible to implement parts of classes in different modules than other parts. This would allow logical grouping of methods for a set of related classes. This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.
Jul 16 2007
This might be an interesting perspective on the kind of harm such an option might cause: http://www.ddj.com/dept/cpp/184401197 Robert Fraser Wrote:It would be nice if it were possible to implement parts of classes in different modules than other parts. This would allow logical grouping of methods for a set of related classes. This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.
Jul 16 2007
Tristam MacDonald wrote:This might be an interesting perspective on the kind of harm such an option might cause: http://www.ddj.com/dept/cpp/184401197 Robert Fraser Wrote:I thought it was possible to do something like: void foo(A a) { } ... a.foo(); I tried it out and it didn't work. Sigh, it would be awesome for many reasons if D did support this. -JoelIt would be nice if it were possible to implement parts of classes in different modules than other parts. This would allow logical grouping of methods for a set of related classes. This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.
Jul 16 2007
That article is indeed well-reasoned, however it fails to take into account other features. In particular (this may come off as Java-fanboy nitpicking): 1. It encourages a large public interface: if functions that depend soley on the "public interface" are non-members, to achieve the same level of accessibility, the initial public interface of a class may need to be expanded. In particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely. 2. The architecture is less nimble for client code. If a function that at one time needed only access to the public interface later needs member access, the non-member function will become a simple proxy for the member function, or, worse, the public interface could be expanded. 3. Sending messages directly to a class is (IMO) clearer, prettier, and better for auto-complete tools & lookup (manual or go-to-definition) than a free function. But to each his own, I guess. That article certainly had some good points. That said, I was thinking about partial class implementation mainly with regards to related virtual functions (i.e. Implementing the same abstract/interface/override function across multiple subclasses all in the same file, while other parts of those clases are defined elsewhere. This will help logically group large clas hirearchies, IMO. Tristam MacDonald Wrote:This might be an interesting perspective on the kind of harm such an option might cause: http://www.ddj.com/dept/cpp/184401197 Robert Fraser Wrote:It would be nice if it were possible to implement parts of classes in different modules than other parts. This would allow logical grouping of methods for a set of related classes. This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.
Jul 16 2007
Robert Fraser wrote:Classes should be small and as minimal as possible. I think this is part of Meyers argument. Anything that can be placed outside the class should be. If it can't because that gives assess to something that could potentially be misused by the world outside the class, thats a case to make it a member. Therefore you get this highly easy to use class that is less bug prone. It knows what is job is, its small so its easy to maintain.That article is indeed well-reasoned, however it fails to take intoaccount other features. In particular (this may come off as Java-fanboy nitpicking):1. It encourages a large public interface: if functions that dependsoley on the "public interface" are non-members, to achieve the same level of accessibility, the initial public interface of a class may need to be expanded.In particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely.This is true. I don't think the example of a point was the best example. Herb Sutter and Bjarne Stroustrup's often argue that if the class variables have not constraints (invariants) then the class should really be a C struct (where everything is public).2. The architecture is less nimble for client code. If a function that at one time needed only access to the public interface later needs member access, the non-member function will become a simple proxy for the member function, or, worse, the public interface could be expanded.But this is a good thing. It means you either have to go back to the drawing board with the class interface or add the non-member into the interface. Its pretty easy to remove a non-member function. Its much harder to remove a function once it becomes part of a class. Note that if you want to extend a class there are other ways to do it (namely using a component). Also if you find your creating proxies, then there was probably something wrong with the design in the first place.3. Sending messages directly to a class is (IMO) clearer, prettier, and better for auto-complete tools & lookup (manual or go-to-definition) than a free function.There is no real point in arguing this. My main argument is based on code refactorbility and design. I find free functions are much more reuseable then member functions, look at algorithms in std (find, sort ect...).But to each his own, I guess. That article certainly had some good points. That said, I was thinking about partial class implementation mainly with regards to related virtual functions (i.e. Implementing the same abstract/interface/override function across multiple subclasses all in the same file, while other parts of those clases are defined elsewhere. This will help logically group large clas hirearchies, IMO.While I'm not against the idea. I think, its a much better idea to have lots of small cohesive component classes rather then a large class. I've seen so many large classes and I've always been able to break them down into small chunks. I must admit though it can sometimes be quicker in the short run to simply throw everything into the same class. Here's some more information: http://www.artima.com/intv/goldilocks.htmlTristam MacDonald Wrote:This might be an interesting perspective on the kind of harm such an option might cause: http://www.ddj.com/dept/cpp/184401197 Robert Fraser Wrote:It would be nice if it were possible to implement parts of classes in different modules than other parts. This would allow logical grouping of methods for a set of related classes. This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.
Jul 16 2007
I want to extend what I was saying about components. If you have a class and you want to break it up into several files based on methods, why not use components instead. You end up with a much more flexible design. ie Say I have: class Boat { public: string GetName(); string GetOwner(); //ect... point GetLocation(); point Facing(); float GetSpeed(); //ect... }; If you went for component design you would probably do something like: class Info { public: string GetName(); string GetOwner(); //ect... }; class PhysicsObj { public: point GetLocation(); point Facing(); float GetSpeed(); //ect... }; struct Boat { Info info = new Info(); PhysicsObj physicsObj = new PhysicsObj(); }; Immediately more reusable and maintainable. Now like you said, you could use mixins. This is a nicety of D however components offer a few advantages as well like a nice way to break up the namespaces, are easier to refactor and allow for more cohesive code. -Joel
Jul 16 2007
janderson wrote:Robert Fraser wrote:There is no real point in arguing this. My main argument is based on code refactorbility and design. I find free functions are much more reuseable then member functions, look at algorithms in std (find, sort ect...).Does that really hold true for D though? Without Koenig lookup, free functions don't have the same flexibility they do in C++. You can write generic template functions like find, sort, etc, where there is a single implementation and callers have to implement the concept it requires. But something like 'toString' as a non-member is pretty much impossible in D. --bb
Jul 16 2007
Bill Baxter wrote:janderson wrote:I didn't realise there was an issue here, before; can you tell me if this is what you are talking about? --- module a.b.c; struct A {...} string toString(A) {...} --- module a.b; import a.b.c; class B { A getInstanceOfA() {...} } --- module b; import a.b; void main() { B b = new B(); auto a = b.getInstanceOfA(); string s = toString(a); // doesn't work } --- In this case, I can see it might be a surprise that it doesn't work, but is there anything worse than that? What's wrong with just importing a.b.c and carrying on? If you did any serious manipulations of 'a', then you would have to declare the type 'A' *sometime*; so you'd end up importing a.b.c anyway... I'm just taking a guess at what you mean, though; if I'm completely off, can you please explain? -- ReinerRobert Fraser wrote:There is no real point in arguing this. My main argument is based on code refactorbility and design. I find free functions are much more reuseable then member functions, look at algorithms in std (find, sort ect...).Does that really hold true for D though? Without Koenig lookup, free functions don't have the same flexibility they do in C++. You can write generic template functions like find, sort, etc, where there is a single implementation and callers have to implement the concept it requires. But something like 'toString' as a non-member is pretty much impossible in D. --bb
Jul 17 2007
Reiner Pope wrote:Bill Baxter wrote:I think that's ok, insofar as what you've written. What you can't do is import more than one version of toString, even if they are unabmiguous w.r.t. the usual overloading rules: ---- module a; struct A { ... } string toString(A) { ... } ---- module b; struct B { ... } string toString(B) { ... } ---- import a; import b; void main() { } ---- That fails just importing the two modules, so I didn't bother actually trying to call the toString methods. The above would work fine if you had the two flavors of 'toString' all consolidated in one module. It would also work fine with 'static import' and explicit use of a.toString and b.toString. But what doesn't work is resolving an overload to different modules based on the argument type. Apparently it's a big hairy deal to implement and the bane of compiler-writers' existence. But C++ allows it. Walter believes it's not necessary, though. And so far he seems right. But it does mean you need to put some things inside classes (like toString) that could be free functions if protection levels were the only concern. --bbjanderson wrote:I didn't realise there was an issue here, before; can you tell me if this is what you are talking about? --- module a.b.c; struct A {...} string toString(A) {...} --- module a.b; import a.b.c; class B { A getInstanceOfA() {...} } --- module b; import a.b; void main() { B b = new B(); auto a = b.getInstanceOfA(); string s = toString(a); // doesn't work } --- In this case, I can see it might be a surprise that it doesn't work, but is there anything worse than that? What's wrong with just importing a.b.c and carrying on? If you did any serious manipulations of 'a', then you would have to declare the type 'A' *sometime*; so you'd end up importing a.b.c anyway... I'm just taking a guess at what you mean, though; if I'm completely off, can you please explain?Robert Fraser wrote:There is no real point in arguing this. My main argument is based on code refactorbility and design. I find free functions are much more reuseable then member functions, look at algorithms in std (find, sort ect...).Does that really hold true for D though? Without Koenig lookup, free functions don't have the same flexibility they do in C++. You can write generic template functions like find, sort, etc, where there is a single implementation and callers have to implement the concept it requires. But something like 'toString' as a non-member is pretty much impossible in D. --bb
Jul 17 2007
Reply to Robert,This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.IIRC this works. Or did I misread you? template Foo() { int get(){ return foo; } void set foo(int i){foo = i;} } class Bar { int foo; mixin foo!(); }
Jul 17 2007
BCS Wrote:Reply to Robert,Yup; that's what I'm doing now.This is already possible via template mixins (sort of, but alias template params are required to access fields), but explicit partials might be quite helpful.IIRC this works. Or did I misread you? template Foo() { int get(){ return foo; } void set foo(int i){foo = i;} } class Bar { int foo; mixin foo!(); }
Jul 17 2007
janderson Wrote:Classes should be small and as minimal as possible. I think this is part of Meyers argument. Anything that can be placed outside the class should be. If it can't because that gives assess to something that could potentially be misused by the world outside the class, thats a case to make it a member. Therefore you get this highly easy to use class that is less bug prone. It knows what is job is, its small so its easy to maintain.I agree so far...I disagree here, because you are making the assumption that there will never be a need to refactor (with D's property syntax, this is somewhat mitigated since a public field can be turned into a method without breaking client code, however if something that was a stack-allocated class later needs to be given dynamic dispatch, this is not as much as possibility). Further, however, this mindset encourages thinking of classes/structs as aggregations of data. For a better example than a point, think about an AST node in a compiler (what I was thinking about when I wrote the OP, though now we're pretty off-topic). A generalized AST node will have a set of children and a parent, and a subclass implementing a conditional expression may have a condition, a then expresson and an else expression. It's possible that all this should be properties with getters and setters. However, if you think more deeply about it (I'd suggest staring at a picture of a bonsai tree...), the parent, condition, then expression and else expression will only be set once, and, depending on how you generate the tree, this may be at construction time. So, having setters for these properties is unnecessary and potentially dangerous if other coders will be using your class. In fact, very few classes I write have explicit getters/setters/properties. Classes, IMO, are aggregations of methods, not data, and the data serves to hold state relating to invocations of those methods. Which, I guess, is the main philosophical difference between the ways we don't use free functions except in very rare situations where they don't fit within a class. There's room for two different kinds of programmers in this world.In particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely.This is true. I don't think the example of a point was the best example. Herb Sutter and Bjarne Stroustrup's often argue that if the class variables have not constraints (invariants) then the class should really be a C struct (where everything is public).Why is it easier to remove a non-member function than a member function? Client code relying on the function will break either way.2. The architecture is less nimble for client code. If a function that at one time needed only access to the public interface later needs member access, the non-member function will become a simple proxy for the member function, or, worse, the public interface could be expanded.But this is a good thing. It means you either have to go back to the drawing board with the class interface or add the non-member into the interface. Its pretty easy to remove a non-member function. Its much harder to remove a function once it becomes part of a class. Note that if you want to extend a class there are other ways to do it (namely using a component).Also if you find your creating proxies, then there was probably something wrong with the design in the first place.Initially, yes. But if you're refactoring your code and something that was a free function needs to become a method (needs access to private members), what other choice do you have (if client code is already using the function)?I didn't mean a large class, I meant a large class _hierarchy_ (of smaller classes). I was thinking specifically about how, in the DMD source code, Walter added the inlineCost method to every AST node in a single file (inline.c), which, IMO, is cleaner than implementing the inlineCost method along with all the other stuff relating to that AST node. Simpler example, to make it clear what I meant (assume these classes are large enough to justify being in separate modules): Version 1 (now): ------------- module animals.core; interface IHungryAnimal { public void eat(); } ---------- module animals.mammals; class Bat : IHungryAnimal { public void fly() { ... } public void eat() { ... } } -------------- module animals.anphibiphans; class Frog : IHungryAnimal { public void ribbit() { ... } public void eat() { ... } } ++++++++++++++++ ++++++++++++++++ ++++++++++++++++ Version 2 (With partial classes): -------------- module animals.mammals; partial class Bat { void fly() { ... } } -------------- module animals.anphibiphans; partial class Frog { void ribbit() { ... } } ------------- module animals.eating; interface IHungryAnimal { public void eat(); } partial class Bat : IHungryAnimal { public void eat { ... } } partial class Frog : IHungryAnimal { public void eat { ... } }But to each his own, I guess. That article certainly had some good points. That said, I was thinking about partial class implementation mainly with regards to related virtual functions (i.e. Implementing the same abstract/interface/override function across multiple subclasses all in the same file, while other parts of those classes are defined elsewhere. This will help logically group large class hierarchies, IMO.While I'm not against the idea. I think, its a much better idea to have lots of small cohesive component classes rather then a large class. I've seen so many large classes and I've always been able to break them down into small chunks. I must admit though it can sometimes be quicker in the short run to simply throw everything into the same class.
Jul 17 2007
Robert Fraser wrote:janderson Wrote:No such assumption is present. In fact, quite the reverse; KISS helps to keep refactoring easy, by dumping unnecessary complexity that impedes change. And also for one more reason, that you touch on further down...Classes should be small and as minimal as possible. I think this is part of Meyers argument. Anything that can be placed outside the class should be. If it can't because that gives assess to something that could potentially be misused by the world outside the class, thats a case to make it a member. Therefore you get this highly easy to use class that is less bug prone. It knows what is job is, its small so its easy to maintain.I agree so far...I disagree here, because you are making the assumption that there will never be a need to refactorIn particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely.This is true. I don't think the example of a point was the best example. Herb Sutter and Bjarne Stroustrup's often argue that if the class variables have not constraints (invariants) then the class should really be a C struct (where everything is public).(with D's property syntax, this is somewhat mitigated since a public field can be turned into a method without breaking client code, however if something that was a stack-allocated class later needs to be given dynamic dispatch, this is not as much as possibility). Further, however, this mindset encourages thinking of classes/structs as aggregations of data.It doesn't, but that's the reason why it's helpful when keeping code amenable to refactoring. Sometimes types are present to express behavior. Those should be designed in terms of operations, with the representaton of their state hidden. Other types genuinely are just collections of data. Such are rarer than beginner programmers tend to think, but more common than many OO textbooks would tend to admit. It's the "mere aggregations of data" that are mostly clearly expressed in terms of data. It's true that once in a while, during evolution of a system, something that was just dumb data turns out to be usefully converted into a smarter object. If you ruthlessly kept your code simple (e.g., by using simple structs where appropriate) then (in C++ at least) it's trivial to make such a change (as C++ has less difference between struct and class than current D). If you build in speculative complexity in case of change, you ironically end up making evolution harder. (This is one of the pieces of XP that I've found has held up well in the face of experience and experimentation.)For a better example than a point, think about an AST node in a compiler (what I was thinking about when I wrote the OP, though now we're pretty off-topic). A generalized AST node will have a set of children and a parent, and a subclass implementing a conditional expression may have a condition, a then expresson and an else expression. It's possible that all this should be properties with getters and setters. However, if you think more deeply about it (I'd suggest staring at a picture of a bonsai tree...), the parent, condition, then expression and else expression will only be set once, and, depending on how you generate the tree, this may be at construction time. So, having setters for these properties is unnecessary and potentially dangerous if other coders will be using your class.Right; a bunch of setters/getters is worse than either (a) a simple data object or (b) an object exposing behavior and hiding unnecessary access to its state.In fact, very few classes I write have explicit getters/setters /properties. Classes, IMO, are aggregations of methods, not data, and the data serves to hold state relating to invocations of those methods.That's the common case; I think Bjarne, Herb, Scott etc. are assuming that you know this, and highlighting the exceptions. -- James
Jul 17 2007
James Dennett wrote:Robert Fraser wrote:I couldn't have put it better. Thanks James.janderson Wrote:No such assumption is present. In fact, quite the reverse; KISS helps to keep refactoring easy, by dumping unnecessary complexity that impedes change. And also for one more reason, that you touch on further down...Classes should be small and as minimal as possible. I think this is part of Meyers argument. Anything that can be placed outside the class should be. If it can't because that gives assess to something that could potentially be misused by the world outside the class, thats a case to make it a member. Therefore you get this highly easy to use class that is less bug prone. It knows what is job is, its small so its easy to maintain.I agree so far...I disagree here, because you are making the assumption that there will never be a need to refactorIn particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely.This is true. I don't think the example of a point was the best example. Herb Sutter and Bjarne Stroustrup's often argue that if the class variables have not constraints (invariants) then the class should really be a C struct (where everything is public).(with D's property syntax, this is somewhat mitigated since a public field can be turned into a method without breaking client code, however if something that was a stack-allocated class later needs to be given dynamic dispatch, this is not as much as possibility). Further, however, this mindset encourages thinking of classes/structs as aggregations of data.It doesn't, but that's the reason why it's helpful when keeping code amenable to refactoring. Sometimes types are present to express behavior. Those should be designed in terms of operations, with the representaton of their state hidden. Other types genuinely are just collections of data. Such are rarer than beginner programmers tend to think, but more common than many OO textbooks would tend to admit. It's the "mere aggregations of data" that are mostly clearly expressed in terms of data. It's true that once in a while, during evolution of a system, something that was just dumb data turns out to be usefully converted into a smarter object. If you ruthlessly kept your code simple (e.g., by using simple structs where appropriate) then (in C++ at least) it's trivial to make such a change (as C++ has less difference between struct and class than current D). If you build in speculative complexity in case of change, you ironically end up making evolution harder. (This is one of the pieces of XP that I've found has held up well in the face of experience and experimentation.)For a better example than a point, think about an AST node in a compiler (what I was thinking about when I wrote the OP, though now we're pretty off-topic). A generalized AST node will have a set of children and a parent, and a subclass implementing a conditional expression may have a condition, a then expresson and an else expression. It's possible that all this should be properties with getters and setters. However, if you think more deeply about it (I'd suggest staring at a picture of a bonsai tree...), the parent, condition, then expression and else expression will only be set once, and, depending on how you generate the tree, this may be at construction time. So, having setters for these properties is unnecessary and potentially dangerous if other coders will be using your class.Right; a bunch of setters/getters is worse than either (a) a simple data object or (b) an object exposing behavior and hiding unnecessary access to its state.In fact, very few classes I write have explicit getters/setters /properties. Classes, IMO, are aggregations of methods, not data, and the data serves to hold state relating to invocations of those methods.That's the common case; I think Bjarne, Herb, Scott etc. are assuming that you know this, and highlighting the exceptions. -- James
Jul 19 2007
Robert Fraser wrote:janderson Wrote:Classes should be small and as minimal as possible. I think this is part of Meyers argument. Anything that can be placed outside the class should be. If it can't because that gives assess to something that could potentially be misused by the world outside the class, thats a case to make it a member. Therefore you get this highly easy to use class that is less bug prone. It knows what is job is, its small so its easy to maintain.I agree so far...In particular the getter/setter/property pattern is encouraged by such a design, and overuse of this pattern can degrade a class to a simple aggregation of data, defeating the purpose of encapsulation entirely.This is true. I don't think the example of a point was the best example. Herb Sutter and Bjarne Stroustrup's often argue that if the class variables have not constraints (invariants) then the class should really be a C struct (where everything is public).I disagree here, because you are making the assumption that there willnever be a need to refactor (with D's property syntax, this is somewhat mitigated since a public field can be turned into a method without breaking client code, however if something that was a stack-allocated class later needs to be given dynamic dispatch, this is not as much as possibility). Your right that this is a difference from C++ and D. However its still possible to make the struct a component of a class. That way you still keep the functionality of the struct. Actually this is the recommend way of solving that problem in C++.Further, however, this mindset encourages thinking of classes/structs as aggregations of data. For a better example than a point, think about an AST node in a compiler (what I was thinking about when I wrote the OP, though now we're pretty off-topic). A generalized AST node will have a set of children and a parent, and a subclass implementing a conditional expression may have a condition, a then expresson and an else expression. It's possible that all this should be properties with getters and setters. However, if you think more deeply about it (I'd suggest staring at a picture of a bonsai tree...), the parent, condition, then expression and else expression will only be set once, and, depending on how you generate the tree, this may be at construction time. So, having setters for these properties is unnecessary and potentially dangerous if other coders will be using your class.Yes this is a reasonable example. The use of getters/setters is useful for restricting use of the data to prevent bugs. Its also useful for changing the integrals of the object but keeping the same interface. For instance if you decide that you wish to change your internal matrix representation to a quaternion and a vector.In fact, very few classes I write have explicit getters/setters/properties. Classes, IMO, are aggregations of methods, not data, and the data serves to hold state relating to invocations of those methods. Which, I guess, is the main philosophical difference between the ways simply don't use free functions except in very rare situations where they don't fit within a class. There's room for two different kinds of programmers in this world.Although you can simulate free functions in these languages they don't make it easy. In my option its not wrong to use a highly OO techniques can get you where u want to go. However if you have a multi-paradigm language like D or C++ you can take advantage of both sides.1) free-functions are not tied to their data types. You can easily change the parameters to meet the goals of the function. For instance if you decide you don't need that class anymore, you may still beable to change the function to something without that class. 2) Consider a function that takes 2 or more classes, which object does it belong to? 3) Templates are a great example of the reusabilty of free functions. It should be possible to replace any free-function with a template or overload (one nasty thing about D is you have to either create different names for overloads or alias them in). 4) When you inherit from a class if you have a lot of publics that should be free functions then your going to have to maintain them in the child class as well. (I also recommend not using inheritance for reuse, but for polymorphic type operations.) 5) Putting a method inside a class gives you access to all of its private data. Now if you choose to go completely though the public data and are rigorous about that then the class will be less error prone because your sub-set of methods you call should be really solid. However how do you tell which is your subset if they are all in the same class? How do ensure that a method doesn't suddenly . Free functions automatically have this constraint. When you've got a solid subset that you don't have to worry about that abstraction any more. 6) Adding member functions that really should be free functions will increase the size of the class. Its harder to unit test large classes. 7) You can't take the class and use it in another project so easily because the member-functions (which should be free) may be coupling it to other things. There's a couple of more points I think I've missed. If you want an in depth look into this I'd recommend Exceptional C++ by Herb Sutter. -- Now I should point out like I said before, if you don't have access to the internals of a class because they are being protected for integrity reasons (this is a good thing) then it should be private.Why is it easier to remove a non-member function than a member function? Client code relying on the function will break either way.2. The architecture is less nimble for client code. If a function that at one time needed only access to the public interface later needs member access, the non-member function will become a simple proxy for the member function, or, worse, the public interface could be expanded.But this is a good thing. It means you either have to go back to the drawing board with the class interface or add the non-member into the interface. Its pretty easy to remove a non-member function. Its much harder to remove a function once it becomes part of a class. Note that if you want to extend a class there are other ways to do it (namely using a component).Normally this would only occur of the interface of the class changes. If the interface of the class does change you'll be told about possible issues like this from the compiler. If it was inside the class, its possible the code code may become invade without your knowledge (since it seems like a reasonable large change to the interface of the class). You can as you suggested always write a wrapper if necessary. However a free-function will normally be doing more then a simple operation. Normally you'll find you'll need to provide a way to modify or read this internal value one way or another from the outside (note I'm not saying directly). I could make the argument the other way what if the class changed you want to do the operations on. Do you remove the method from the other class and create a new one in the new class?Also if you find your creating proxies, then there was probably something wrong with the design in the first place.Initially, yes. But if you're refactoring your code and something that was a free function needs to become a method (needs access to private members), what other choice do you have (if client code is already using the function)?This walking-the-OO-line a well known C++ verse Java debate. I can't say I side with the Java side because most of the opponents haven't used C++ at least in this way (the same can't be said about C++ programmers). I used to be very OO but personally I've come to see this sort of coding works a lot better for me. Its a big learning step, which requires an adjustment in thinking. Its also a common interview question. So I'm no saying your wrong, I'm just saying this is why I code this way. Either way its always good to have a overall plan when you code. And its great that your thinking about this sort of high level stuff. This is the sort of thing I think "style-guides" should be about rather then how many spaces you put in front of your methods ;) I hope that helps.But to each his own, I guess. That article certainly had some good points.
Jul 18 2007
Hi Joel, This is quite the interesting thread; these are the sort of issues I'd like to confront before I start on a larger D project.Yes this is a reasonable example. The use of getters/setters is useful for restricting use of the data to prevent bugs. Its also useful for changing the integrals of the object but keeping the same interface. For instance if you decide that you wish to change your internal matrix representation to a quaternion and a vector.Indeed, my company style guide requires that getters/setters exist for all variables, most of which are private and used internally by other methods of the class. These and the constructors are the only methods allowed to refer to the variables directly. Personally, I find this going _way_ overboard (plus, the class internals start to get out of sync if legacy properties are supported for non-existent fields), but it's actually saved me once or twice.4) When you inherit from a class if you have a lot of publics that should be free functions then your going to have to maintain them in the child class as well. (I also recommend not using inheritance for reuse, but for polymorphic type operations.)Agreed. IMO, composition is almost always preferable to inheritance if virtual dispatch is not necessary.This walking-the-OO-line a well known C++ verse Java debate. I can't say I side with the Java side because most of the opponents haven't used C++ at least in this way (the same can't be said about C++ programmers).It's true I haven't used C++ outside a couple projects for school. I have used C before (since the tender age of 12; it was the first language I learned), and found the organizational benefits offered by OO very compelling. But since I haven't used a strongly-typed language supporting both free functions and class methods for any appreciable project (well, I'm working on one medium-sized one in D right now using only classes so far), I don't really know what I'm talking about. So I'm willing to give this whole free-function thing a try and see how it goes. Thanks for walking me through all that.Its also a common interview question.So am I supposed to say "OO all the way!" or "free functions need love, too"? - Fraser
Jul 19 2007
Robert Fraser wrote:It depends where your interviewing, the style the company uses. They simply want to know how well you'll work the the teams style. Most C++ companies will want you to explain when free-functions are useful and when methods are more useful. To show that you understand how to maximize their benefits and design strongly typed code. "OO all the way!" would be a big mark (at the least) against getting a more senior position.Its also a common interview question.So am I supposed to say "OO all the way!" or "free functions need love, too"?- Fraser
Jul 20 2007
Robert Fraser wrote:Hi Joel, Indeed, my company style guide requires that getters/setters exist for all variables, most of which are private and used internally by other methods of the class. These and the constructors are the only methods allowed to refer to the variables directly. Personally, I find this going _way_ overboard (plus, the class internals start to get out of sync if legacy properties are supported for non-existent fields), but it's actually saved me once or twice.Personally I hate writing tons of getter setters. Its like smashing my head against concrete. I hate having to write 5 lines simply to add a new variable. Then if anything changes you've got 5 more lines per getter/setter to maintain. So I try to keep the number of setters low, most data private and have actually useful functions for managing them instead. Sometimes its possible to use a strut component (as a param to a setter or getter) to minimize getters setters. Many times I return a object which can be modified. That object has its own set of validations. Although I can see setter/getter they add superior interface benefits. I think programming is a balancing act in every reguard. -Joel
Jul 20 2007
I agree, but with a good IDE, this is a non-issue. I refer to the fields directly when I'm coding and run a 30 second refactoring in Eclipse to generate getters and setters and refer to them instead of the field before I do a check-in. Still, there's something to be said for abstracting field access within a class (although, again, D's property syntax makes this unnecessary.) janderson Wrote:Robert Fraser wrote:Hi Joel, Indeed, my company style guide requires that getters/setters exist for all variables, most of which are private and used internally by other methods of the class. These and the constructors are the only methods allowed to refer to the variables directly. Personally, I find this going _way_ overboard (plus, the class internals start to get out of sync if legacy properties are supported for non-existent fields), but it's actually saved me once or twice.Personally I hate writing tons of getter setters. Its like smashing my head against concrete. I hate having to write 5 lines simply to add a new variable. Then if anything changes you've got 5 more lines per getter/setter to maintain. So I try to keep the number of setters low, most data private and have actually useful functions for managing them instead. Sometimes its possible to use a strut component (as a param to a setter or getter) to minimize getters setters. Many times I return a object which can be modified. That object has its own set of validations. Although I can see setter/getter they add superior interface benefits. I think programming is a balancing act in every reguard. -Joel
Jul 20 2007
James Dennett Wrote:That's the common case; I think Bjarne, Herb, Scott etc. are assuming that you know this, and highlighting the exceptions. -- JamesIndeed, for the exceptional (mostly-data) types, working with external functions is probably better coding style. But this doesn't preclude partial class implementation.
Jul 18 2007
Thinking about it more, though, this might break the existing module concept, so... proposal retracted. Robert Fraser Wrote:James Dennett Wrote:That's the common case; I think Bjarne, Herb, Scott etc. are assuming that you know this, and highlighting the exceptions. -- JamesIndeed, for the exceptional (mostly-data) types, working with external functions is probably better coding style. But this doesn't preclude partial class implementation.
Jul 18 2007