www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Partial class implementation

reply Robert Fraser <fraserofhtenight gmail.com> writes:


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
next sibling parent reply Tristam MacDonald <swiftcoder gmail.com> writes:
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
next sibling parent janderson <askme me.com> writes:
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.
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. -Joel
Jul 16 2007
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
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
parent reply janderson <askme me.com> writes:
Robert Fraser wrote:
 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.
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.
 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.html
 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
next sibling parent janderson <askme me.com> writes:
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
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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
parent reply Reiner Pope <some address.com> writes:
Bill Baxter wrote:
 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
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? -- Reiner
Jul 17 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Reiner Pope wrote:
 Bill Baxter wrote:
 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
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?
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. --bb
Jul 17 2007
prev sibling next sibling parent reply BCS <ao pathlink.com> writes:
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
parent Robert Fraser <fraserofthenight gmail.com> writes:
BCS Wrote:

 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!(); }
Yup; that's what I'm doing now.
Jul 17 2007
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
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 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.
 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).
Why is it easier to remove a non-member function than a member function? Client code relying on the function will break either way.
 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)?
 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.
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 { ... } }
Jul 17 2007
next sibling parent reply James Dennett <jdennett acm.org> writes:
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 will never be a need to refactor
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...
 (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
parent janderson <askme me.com> writes:
James Dennett wrote:
 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 will never be a need to refactor
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...
 (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
I couldn't have put it better. Thanks James.
Jul 19 2007
prev sibling parent reply janderson <askme me.com> writes:
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
 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). 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.
 
 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).
Why is it easier to remove a non-member function than a member function? Client code relying on the function will break either way.
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.
 
 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)?
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?
 
 But to each his own, I guess. That article certainly had some good
 points.
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.
Jul 18 2007
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
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
next sibling parent janderson <askme me.com> writes:
Robert Fraser wrote:
 Its also a common interview question.
So am I supposed to say "OO all the way!" or "free functions need love, too"?
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.
 
 - Fraser
Jul 20 2007
prev sibling parent reply janderson <askme me.com> writes:
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
parent Robert Fraser <fraserofthenight gmail.com> writes:
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
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
James Dennett Wrote:

 That's the common case; I think Bjarne, Herb, Scott etc. are
 assuming that you know this, and highlighting the exceptions.
 
 -- James
Indeed, 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
parent Robert Fraser <fraserofthenight gmail.com> writes:
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.
 
 -- James
Indeed, 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