0

I'm asking this question because I believe they did it for a very good reason and that most people do not use it properly, well from my experience in industry so far anyway. But if my theory is true then I'm not sure why they included the private access modifier...?

I believe that if default access is properly used it provides enhanced testability whilst maintaining encapsulation. And it also renders the private access modifier redundant.

The default access modifier can be used to provide the same affect by using a unique package for methods that need to be hidden from the rest of the world, and it does this without compromising testability, as packages in a test folder, with the same are able to access all the default methods declared in a source folder.

I believe this is why Java uses package access as 'default'. But I'm not sure why they also included private access, I'm sure there is a valid use case...

newlogic
  • 807
  • 8
  • 25
  • 3
    private is key when classes have internal methods that no other class must touch. I'm not sure whats so controversial about that. Are you suggesting private has no purpose?! In which case I strongly disagree – Richard Tingle Dec 02 '13 at 14:50
  • Problem comes when trying to test private methods easily, and you can achieve the effect of private members using default access, by creating another package. – newlogic Dec 02 '13 at 14:51
  • 1
    yes, but what about methods that need to be accessable to some, closely related classes but not the world at large? Also, you seem to be trying to simulate private by throwing away package access. – Richard Tingle Dec 02 '13 at 14:52
  • Sure you can achieve this by making another package to contain that classes, which isolates it from the world, even with default access level members, so Class X containing private methods and members would become, Y.X with default level methods and members. – newlogic Dec 02 '13 at 14:56
  • My understanding is that Y.X is a completely seperate package from Y (except that by convention they are related), package access Y.X cannot be accessed from Y or vice versa – Richard Tingle Dec 02 '13 at 14:57
  • Also, [koljaTM](http://stackoverflow.com/a/20331178/2187042) makes a fair point, you shouldn't be testing any of this stuff anyway because it's internal details; subject to change at any time so long as the external details remain the same – Richard Tingle Dec 02 '13 at 15:01
  • Sure i'm not suggesting they are related, i'm just creating a new package which will contain my original class, in order to give the code a namespace separate from all other code, that way I don't need private methods and members, I can simply use default access, and thus simplify testing without reducing encapsulation. If we use the same package names across test and source folders, we can then access all the source code, from within our test folders, hence full visibility without breaking encapsulation! – newlogic Dec 02 '13 at 15:03
  • I disagree all code needs to be tested, data is always interpreted and therefore our interpretations need to be documented and tested in a repeatable way. – newlogic Dec 02 '13 at 15:05
  • Assuming you have 1 class per package (all classes that have private methods anyway) then you completely destroy encapsulation because almost all methods have to be public – Richard Tingle Dec 02 '13 at 15:06
  • If your program is dependant on the specific behaviour of private methods (as opposed to the behaviour of non private methods which may call private methods) then you are writing extremely brittle programs (I'm not sure how you would even achieve that, but it's very bad regardless) – Richard Tingle Dec 02 '13 at 15:07
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/42347/discussion-between-richard-tingle-and-user1037729) – Richard Tingle Dec 02 '13 at 15:08
  • Sorry I can't access chat whilst at work, but I'd be happy to continue the conversation some other time? – newlogic Dec 02 '13 at 15:12
  • Fair enough, well in concludion; It should be possible to swap out the entire internal structure of a class without it mattering (for example to a more efficient form that does the same job). Unit tests test that the new implementation does the same thing as the old implementation. If you make your unit tests dependant on the internal structure you are testing the wrong thing – Richard Tingle Dec 02 '13 at 15:14
  • The aim of testing to ideally cover 100% of the code, this is harder when trying to hit low level code with high level tests, this is what I see unit testing is all about, other wise why don't we just user test systems? I suppose its all relative really... – newlogic Dec 02 '13 at 15:30
  • Using default access modifiers and additional packages, and testing all your code will give you flexibility to change your implementation at any level, not just the level at which you defined your original high level test, I think your way works in theory when all components are perfectly designed and do not need to change. – newlogic Dec 02 '13 at 15:50

2 Answers2

2

I agree about using the default (package private) modifier for stuff that is to be accessed by tests, but I don't agree about private being unnecessary. Even for tests there is a lot of stuff that is not needed to be visible.

For a good test, implementation details are unnecessary and should not be visible outside the class. The more "whitebox" a test is, the more fragile it is. I usually limit the default modifier to fields I expect to be set via dependency injection and set manually in a test. (I could also use constructor injection and get rid of this, but this is more convenient.)

koljaTM
  • 10,064
  • 2
  • 40
  • 42
0

I propose little thought-experiment. Consider this code:

public void promoteUser(User user)
{
    int newRank = computeNew(user);
    user.setRank(newRank);
}

private int computeNewRank(User user)
{
    return user.getRank() + 1;
}

One might feel computeNewRank should be tested (real implementation might do lot more stuff). But let's forget that for a moment and through the magic of inlining do this:

public void promoteUser(User user)
{
    int newRank = user.getRank() + 1;
    user.setRank(newRank);
}

The beauty of this experiment is that it applies to private methods of any size. You can always imagine yourself inlining private member and asking yourself "What do I really want to test here?". Is it the private method itself or perhaps new class/component with brand new functionality that's disguised as private method? The point is, you should rarely (if ever!) need to test private (or even package/internal) members. To outside world, to your contract consumers those are all irrelevant details.

Now, of course we could replace everything with system tests. But then how your regular work flow would look like? What if in order to test the rank promotion code you'd have to log user, register for session, wait 3 minutes, enter promotional code, receive sms, confirm... You see my point.

It's good to remember that unit tests are for you, not the other way around. You can bend them, adjust them, make them fit so that you can deliver software of better quality. Thier purpose is not to help you achieve some magical goal of 100% coverage, but rather to give you immediate feedback on what you're doing so that you can react more quickly to bugs and failures you will encounter. Or in other words, to improve your productivity.

k.m
  • 30,794
  • 10
  • 62
  • 86
  • I understand the concept of maintaining a contract, however if this contract is broken isn't it better to isolate the cause efficiently, and more lower level tests would help this. Implementations can be large and complex, ideally they shouldn't be but often they are, at least default access would allow developers to write tests in the first place which would highlight the bugs with more precision, if the implementation needs to change then change it and write new tests... – newlogic Dec 02 '13 at 17:14
  • Also this means that these implementations can be dissected, hence the implementation can be changed at any level. – newlogic Dec 02 '13 at 17:38
  • I think is private is useful for fields, but not for methods. – newlogic Dec 03 '13 at 10:32