tags:

views:

82

answers:

4

For simplicity let's say I have two classes: a class called Car and a collection class called Tires. Tires is a nested class inside the Car class. I want to give my clients access to the collection - i.e. they should be able to iterate over it:

foreach( var tire in car.tires ) ...

and access each tire's properties. But I don't want to give them access to the methods in tires. I want all access to behavior to come through the Car class:

car.FillTires();

Scenario 1: Tires class is public with methods private: Car class can't access Tire methods.
Scenario 2: Tires class is private with methods public: I can't expose the collection to my clients.
Internal doesn't work for me either as I don't want to grant other classes in the assembly access.

+2  A: 

Create an interface with only the methods you wish to clients to access and expose a collection of the interface type through your class's methods.

Asaph
Good answer, however the class itself has to be internal/private.
Danny Varod
That's not a problem for his suggestion. It's perfectly valid to publicly expose an instance of a private class if you do it only through a public interface.
Joren
I didn't say it is a problem, I meant it needs clarifying in the answer.
Danny Varod
Ah, I misunderstood then, sorry.
Joren
+2  A: 

Option 1: You could put Car in a separate assembly and use scenario 1ish (Tire could be public + methods internal).

Option 2: You could make Tire immutable (like String) and let consumers do what ever they want with tires, but they can not change the tires within the car (make the tires collection's property readonly but do not make the private backing field readonly).

Danny Varod
A: 

You may write class FakeTire that extends Tire.

Override FillTires to throw an exception in FakeTire class. Car has a list of real tires, but it's Tires property creates a list of Faketires to give out. Kindof like a read only collection.

CaptnCraig
As Joren suggests above, this is less than ideal. It will accomplish the goal of enforcing the rules you want, but still will let you think you are able to change the faketires when you actually cannot.
CaptnCraig
+3  A: 

A simple solution is to re-evaluate if Tires should be an inner class. If you make it a normal class you can simply use internal and public to differentiate between what clients and what Car can access.

If there is an unrelated reason why Tires should be an inner class, expose only a read-only version:

  • Expose a copy like CMP suggests (doesn't need to be a different class, although it's indeed better to, so you can throw exceptions). I like this suggestion the least, because it's inelegant to have a class that looks like you can mutate it while actually you can't.
  • Make the whole class immutable like Danny suggests, which is effective and neat, but makes it less convenient to deal with (needing to overwrite the old instance in Car with a new one every time you want to change something.)
  • Expose the class only through a read-only interface like Asaph suggests. I like this best because it allows Car complete control while limiting public access without any inconvenience or copying.
Joren