www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Partial class implementation

reply Robert Fraser <fraserofhtenight gmail.com> writes:
Random, C#-inspired thought:

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:

 Random, C#-inspired thought:
 
 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:
 
 Random, C#-inspired thought:

 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:
 
 Random, C#-inspired thought:
 
 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

nitpicking):
 
 1. It encourages a large public interface: if functions that depend

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:

 Random, C#-inspired thought:

 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 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