July 16, 2005
Hi,

>Much, thanks for the explanation.

Sure thing.

>However, private and protected inheritance seem like very "niche" and complex features for very little benefit.  In addition, it might not be possible to implement well in D, as a private function cannot be virtual - and changing a function's type from public to private when inherited wouldn't fly, given D's inbuilt "virtual" determination...

Can anyone who knows about the compiler enough confirm this?

--AJG.



July 16, 2005
Hi there,

>I don't know how hard would it be to implement, but I think there is a better way around it:
>
>class A { /* stuff */ }
>
>class B { private A a; /* other stuff */ }
>
>I think that makes more sense, because if you want to hide A's interface then B probably is not an A, it just uses it.
>
>Even the DeviceContext example, Graphics probably _uses_ a device
>context to draw stuff, but it's not really a device context.
>Ofcourse I'm just making assumptions here, but my feeling is if you need
>to hide the super classes interface then you probably are not of the
>same type as the super class.

Yes, I mentioned that this is a possibility, but it's not ideal. It's lacking in functionality because:

1) Graphics (B) would not be able to extend or override DeviceContext (A).
2) Graphics (B) has limited access to DeviceContext (A). All protected members
are gone.
3) If DeviceContext (B) was meant to be derived from, or is simply abstract,
then you need to create an intermediate class -hardly an elegant solution.

In essence, there is a marked difference between private containment (your suggestion), which is also known as "HAS-ONE-OF," and private inheritance, aka "IS-IMPLEMENTED-IN-TERMS-OF." It's incorrect to assume that just because a lower class doesn't want to expose a super class' members it should not be one of the same type. In this case, it's about the API and how you want your object accessed. This is a great facility for that, and makes things simpler.

--AJG.





July 16, 2005
Hi Andrew,

Thanks for your suggestion. It's pretty amazing that the random example I picked actually turns out to be implemented and it requires the _exact_ thing I was talking about. I feel validated for some reason.

Indeed the package attribute is of _some_ help and could be hacked in to achieve similar functionality (to what we need, true private inheritance). There are various problems though:

1) Package access is semantically confusing at the moment. Nobody's really sure
what it's supposed to do. It's incomplete and unreliable. Or at least:
2) Package access is buggy.
3) Package access is still "friend" access. If you have your classes in the same
file you lose the effect.
4) You need to modify the super class to hack the thing in. What if you can't
edit that class? Why should you have to modify a super class for the behaviour
of some unknown lower class? What if you don't have proper access to the code?
Or, if an ABI eventually matures, what if the source is not even there? For that
you need private inheritance. It's elegant and simple.

As a side note, I checked out the harmonia page and it looks really awesome. If I understand correctly, there are no OS-widgets at all? That's pretty impressive.

--AJG.


>Try this:
>
>From Harmonia:
>---------------------------------------------------
>module harmonia.ui.native.win32graphics;
>
>class NativeGraphics
>{
>   package
>   {
>      // ... various native objects ...
>      void nativeDrawRect(rect rc) { .... }
>   }
>}
>---------------------------------------------------
>module harmonia.gx.graphics;
>
>class Graphics: NativeGraphics
>{
>    void drawRect(rect rc)
>    {
>      nativeDrawRect(rc);
>    }
>}
>
>--------------------------
>
>void testDraw(Graphics g)
>  {
>    g.nativeDrawRect(place);
>//  Error: class harmonia.gx.graphics.Graphics
>//  member nativeDrawRect is not accessible
>  }
>
>---------------------------


July 16, 2005
Hi there,

>But... but... isn't this exactly what interfaces are for? Define an IGraphics interface with DrawCircle(), DrawSquare() etc, have your Graphics class implement that interface, and give an IGraphics to client code instead of a Graphics.

This is yet another incomplete way to solve the problem.

1) This introduces redundancy (rarely good in code), in vain. You must define the interface and then implement it, maintaining the same thing twice, despite the fact that there will likely only ever be one implementation. I understand the use of interfaces for situations where at least they'll be useful multiple times, but this is hardly the case.

2) Graphics (and thus, DeviceContext) are now essentially sealed. Neither can be extended. I guess you could inherit from IGraphics and then use that, but see point 1. Even then, the actual implementations would be locked and unavailable for reuse.

3) Also, what do you mean give an IGraphics? You can't just give an IGraphics, it would be useless to users. At some point, they're going to need to initialize a _real_ Graphics and use that. Then you get the whole problem back again, since DeviceContext is exposed anew.

The pattern I see is that the interface approach is usually useful only when the implementation is not relevant and not required. In this case, the implementation is important, so it doesn't work well. I want to use and keep the implementation available through the object hierarchy, but with the _right_ access modifier (hence, the need for private).

>I meant "what it means semantically", rather than what the syntax looks like. Public inheritance means "Derived is substitutable for Base". Private inheritance means "Derived reuses code from base without exposing that fact in its interface". What does protected inheritance mean?

I'll take a shot: "Derived reuses code from base without exposing that fact in its interface, while keeping the interface open for subclassing."

Cheers,
--AJG.


July 16, 2005
In article <dba19s$mqs$1@digitaldaemon.com>, AJG says...
>
>>But... but... isn't this exactly what interfaces are for?
>
>1) This introduces redundancy (rarely good in code), in vain. You must define the interface and then implement it, maintaining the same thing twice, despite the fact that there will likely only ever be one implementation.

It's not a major burden in practice. The big risk with redundancy is things getting out of sync, and the compiler will let you know if this happens with interfaces and their implementations.

Maybe we're coming from different places; I'm mostly doing agile C# development these days, and interfaces almost always have at least one "other" implementation, even if it's only a mock or stub for unit-testing client code.

>2) Graphics (and thus, DeviceContext) are now essentially sealed. Neither can be extended.

Why not?

# interface IGraphics { void DrawSquare(); }
# class Graphics : IGraphics { void DrawSquare() {} }
#
# // Dammit, now I need to draw teapots too. OK...
#
# interface IMyGraphics : IGraphics { void DrawTeapot(); }
# class MyGraphics : Graphics, IMyGraphics { void DrawTeapot() {} }

>3) Also, what do you mean give an IGraphics? You can't just give an IGraphics, it would be useless to users.

I mean that while you're probably givin them a Graphics *object*, the argument and/or retval types they see will be IGraphics. They don't know that it's a Graphics, so they can't see any of the implementation bits they shouldn't.

>I'll take a shot: "Derived reuses code from base without exposing that fact in its interface, while keeping the interface open for subclassing."

In practice, I've found that (for C#/Java) interfaces work fine. For C++ you'd
normally either use composition or turn things around and have
DeviceContextGraphics (implementation) derive from Graphics (interface), using
something like the GoF Template Method pattern and/or the "Non Virtual
Interface" idiom (see e.g. http://www.gotw.ca/publications/mill18.htm)

cheers
Mike


Next ›   Last »
1 2
Top | Discussion index | About this forum | D home