Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Private Classes - Same or Separate Files?

by Masem (Monsignor)
on Mar 06, 2002 at 14:54 UTC ( #149695=perlmeditation: print w/replies, xml ) Need Help??

Say I have a class My::A; at the time of the development cycle, it's determined that it would a good idea that there be some class My::B which can only be created through My::A and manipulated by My::A; My::B objects are not passed outside of My::A code. So for all effective reasons, My::B can be considered a private class of My::A.

Now, in C++ or Java, you can use the combination of access restrictions on classes and/or internal classes such that My::B would have been sufficiently hidden from any external code save for My::A. Typically, the My::B class would be in the same file as My::A declarations and definitions.

In perl, it's nay impossible to hide such a class (TTBOMK). So there's two possible options I can see: either put the definition of My::B in My::A's file as:

# ./My/ package My::B; # ... # ... package My::A; # ... # ...
or to put B into it's own file:
# ./My/ package My::A; use My::B; # ... # ... # ./My/ package My::B; # ... # ...
In neither case, My::B isn't protected as the user would still be able to gain access to My::B, so worrying about this issue is a forgone conclusion. Thus, it's now mostly a semantics question -- which is the better or preferred way of doing this? The only advantage that it would see to me is using separate files, it's more obvious during use statements that the user is bringing My::B into the block, as opposed to the single file approach; of course, this again makes My::B more accessible that it might seem.

(Now of course, I put the cavaet that such hiding may be determined by the initial design, but down the road, it may be determined that allowing the user to inherit from My::B may be useful or important, so the fact that such code is not hidden by perl to start may be a good thing.

Update I should state that I fully understand that I cannot 'protect' my class from being instaniated as demerphq points out (and I like his analogy as well). Given that, I'm more looking for how I place the class def into files as to indicate to the user as best as possible that they really shouldn't instaniate the class, and/or to make it fit the perl philosophy more.

Dr. Michael K. Neylon - || "You've left the lens cap of your mind on again, Pinky" - The Brain
"I can see my house from here!"
It's not what you know, but knowing how to find it if you don't know that's important

Replies are listed 'Best First'.
Re: Private Classes - Same or Separate Files?
by demerphq (Chancellor) on Mar 06, 2002 at 15:07 UTC
    Well, my first thought is: you cant do this. period.

    The reason being is anyone, anywhere can do

    my $x=bless {},"My::B";
    and it doesnt matter what else you have done or do.

    Perls philosophy is "Id prefer you stay out of my living room because I ask you to, not because I have a shotgun." so you arent allowed a shotgun.

    But i was thinking you could do something simple, a basic caller check to see if the calling sub originated in a module that @ISA "My::A". If it isnt then you could fail, if it is then everything is happy. Something like the following untested snippet

    croak "Must be called from a My::A class or subclass." unless UNIVERSAL::isa(scalar caller(),"My::A");
    Apologies if im not communicating very well, im coming down with the flu.

    I think I sortof answered a question that Masem wasnt asking. :-) If the issue is regarding an aesthetic descion in how to indicate usage through form I would go (and have often done so) with having the 'private' class defined in the same file as the 'public', but at the end of file. Perhaps following some kind of comment that indicates that the contained classes are 'private' utility classes employed by the 'public' one.

    Yves / DeMerphq
    When to use Prototypes?
    Advanced Sorting - GRT - Guttman Rosler Transform

(jeffa) Re: Private Classes - Same or Separate Files?
by jeffa (Bishop) on Mar 06, 2002 at 14:59 UTC
    I personally like for each class to be in it's own file. That way, a user of your modules can (hopefully more) easily find what they are looking for when the time comes to dredge through the source code. This also makes documention easier to find as well. Document everything! Even if you are just saying "Don't instantiate this class!".


    (the triplet paradiddle with high-hat)

      I'm with jeffa least in the general case.

      I do distinguish between "defining the class" and "diddling around in a class I've already gotten defined". In the latter, one might shift into the class to (re)define something or run some code in a different name space.


(tye)Re: Private Classes - Same or Separate Files?
by tye (Sage) on Mar 06, 2002 at 16:16 UTC

    I'd probably go with My::A::B or even My::A::_B [ because a leading "_" is standard Perl "style" for "private, please do not touch" ].

    If the code is very small, I'd put it all in one file. If not, I'd put it in a separate file. [ Though size is not the only issue. If My::A::B's code is easier to maintain by keeping it in the same file or vice versa, then that is a good thing to base your decision on. I like keeping it in the same file as an indication that the code is private to My::A, but would expect that consideration is often outweighed. ]

    I'd also put it in a separate file if there is a possibility of using My::A without ever using My::A::B. In that case I'd be sure to require My::A::B only when I decide I need it and never use it.

    Personally, I'd hate to see code in My::A::B's constructor that tries to enforce that only code from My::A may call it. [ I tried to come up with a short justification for my feelings here, but failed. There are a lot of aspects to it. Such protection usually can be gotten around so you are just making it a little harder. So why waste the cycles? Why add that extra code that can be a source of bugs? I've had to work around bugs in modules and finding code like this that makes such work-arounds harder just makes you want to shoot the module author for spending that time writing that code rather than spending time on real improvements so maybe you wouldn't have to be working around the problem... ]

    [ Updated. ]

            - tye (but my friends call me "Tye")
      Indeed it seems that if you write code that ensures it is only run by the right parent, it defeats one of the main purposes of encapsulating it as a package. Code reuse is a good thing, not an evil to be guarded against.
Re: Private Classes - Same or Separate Files?
by simon.proctor (Vicar) on Mar 06, 2002 at 15:15 UTC
    As mentioned above, the access restrictions in Perl are more about manners than the loaded shotgun of Java or C++. However, in looking at your post it drew a few parallels (for me at least) with the way classes operate in Java. In otherwords, only one public class per file.

    Personally I don't think theres anything wrong in having more than one class per file provided one class (and one only) is considered public, as in Java. The others are then private (manners permitting) and only exist to support the public class. And thats the important point. If My::B were to support anything other than My::A then it would have its own file.

    I can think of a few examples of where this is done, HTML::Template being one of them (if memory serves me correctly at least).
      I like it best when the visible file names match the public functionality of the Classes. Too many inner working files just ends up being a headache and doesn't tell people using your classes anything useful.
      ()-()                                                      ()-()
       \"/    DON'T   BLAME   ME,   I  VOTED   FOR    PACO!       \"/
        `                                                          ` 
Re: Private Classes - Same or Separate Files?
by dragonchild (Archbishop) on Mar 06, 2002 at 15:17 UTC
    I agree with demerphq. Enforce restrictions through code, not organization. If Private::B should only be instantiated from the Public::A hierarchy, make that a requirement, document it, and enforce it in the constructor of Private::B. (With appropriate comments, of course!)

    I can think of two very good reasons to do this:

    1. Private::B might develop into a hierarchy of its own, either as the root parent or as some child of an abstract class you don't know you need right now.
    2. There may be some Private::C that, while not sharing an abstract parent with Private::B, might make sense to have in some Private:: hierarchy. For example, you might want to restrict that all Parser::'s can only be instantiated by a Reader::. There might not be an abstract parent (though I would think there should), but there definitely should be a separate hierarchy ...

    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      I agree with demerphq. Enforce restrictions through code, not organization.

      Actually my point was that afaict in Perl enforcing such restrictions can only be done through code. But it was not that I think such a thing is a good idea.

      Generally speaking I prefer that code doesnt restrict me in any ways that arent absolutely necessary. Just because someone cant think of a good reason why I would want to do X with their module doestn mean that I can think of one, and if I do im gunna be mighty unimpressed that they went out of their way to stop me, especially if there isnt a damn good reason why.

      Yves / DeMerphq
      When to use Prototypes?
      Advanced Sorting - GRT - Guttman Rosler Transform

Re: Private Classes - Same or Separate Files?
by broquaint (Abbot) on Mar 06, 2002 at 17:52 UTC
    I'm not sure how helpful this will be towards your end goal but you might want to check out miyagawa's Attribute::Protected which implements C++/Java style encapsulation of methods (pity it doesn't do package level encapsulation though ;-). It might go part of the way to protecting My::B to the desired level.
    # NOTE: slightly modified code from the docs package SomeClass; use Attribute::Protected; sub new { return bless {}, shift } sub foo : Public { } sub _bar : Private { } sub _baz : Protected { } sub another { my $self = shift; $self->foo; # OK $self->_bar; # OK $self->_baz; # OK } 1; package DerivedClass; @DerivedClass::ISA = qw(SomeClass); sub yetanother { my $self = shift; $self->foo; # OK $self->_bar; # NG: private method $self->_baz; # OK } 1; package main; my $some = SomeClass->new; $some->foo; # OK $some->_bar; # NG: private method $some->_baz; # NG: protected method


Re: Private Classes - Same or Separate Files?
by Juerd (Abbot) on Mar 06, 2002 at 18:20 UTC
    Like tye, if B is strongly related to A, but useful elsewhere, I call the package My::A::B.
    If, for some reason, it should _ONLY_ be called by A, I prefix an underscore, just like "private" methods.

    I put everything that's large in separate files, but a simple class needed only once can as well be in the same file. I even switch packages multiple time to bundle the class to the place where it's used:

    package My::A; ... package My::A::_B; sub TIESCALAR { return bless \(my $foo = $_[1]), $_[0] } sub FETCH { ${ $_[0] } } sub STORE { ${ $_[0] } = $_[1] } package My::A; sub method { my ($self, @foo) = @_; tie my $bar, 'My::A::_B', $foo[0]; ... } ...

    I'd like some package NAMESPACE BLOCK syntax ;)

    ++ vs lbh qrpbqrq guvf hfvat n ge va Crey :)
    Nabgure bar vs lbh qvq fb jvgubhg ernqvat n znahny svefg.
    -- vs lbh hfrq OFQ pnrfne ;)
        - Whreq

      The package keyword is lexically scoped, so you can do
      package My::A; ... { package My::A::_B; sub TIESCALAR { return bless \(my $foo = $_[1]), $_[0] } sub FETCH { ${ $_[0] } } sub STORE { ${ $_[0] } = $_[1] } } sub method { # Is &My::A::method. my ($self, @foo) = @_; tie my $bar, 'My::A::_B', $foo[0]; ... } ...
Re: Private Classes - Same or Separate Files?
by perrin (Chancellor) on Mar 06, 2002 at 17:30 UTC
    I vote for one file. Treat it like a data structure rather than a first-class object. You can define it anywhere in the file if you wrap it in a BEGIN block. I've seen merlyn do that in some of his articles.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://149695]
Approved by root
[chora_sid]: hi

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2018-02-24 14:39 GMT
Find Nodes?
    Voting Booth?
    When it is dark outside I am happiest to see ...

    Results (310 votes). Check out past polls.