简体   繁体   中英

Encapsulation of reference types inside a collection

I declared a class with several properties

class Soil
{
  public double AnglePhi { get; set; }
  public double AngleDelta { get; set; }
  .
  .
  .
}

Now to manipulate a collection of them I built another dedicated class, only for this reason.

class Soils
{
  private const Byte numberOPredefined = 10;   
  private IList<Soil> soils;

  public Soil this[ushort i]
  {
    get { return new Soil() { AngleDelta = soils[i].AngleDelta, ... }; }
    set { if (i > numberOPredefined) soils[i] = value; }
  }
  .
  .
  .
}

The logic behind this, is to protect somewhat from direct manipulation of the properties of each Soil instance. Give a copy in getter, ask for a "whole" soil object in setter.

From what I red thus far, other solutions could be:
make Soil class immutable,
return a ReadOnly List (but then the reference types inseide can be manipulated)
turn Soil class to struct (simple),
augment Soil class with some logic (methods etc).

I would like to ask if the above "solution" has any value at all, or is ill-defined.

This is a typical situation I think, eg having a collection of reference types and want to encapsulate them . What is the typical frame of thinking in these situations ?

EDIT :
Ok, after reading the answers I modified the solution to this

class Soil
{
  private readonly double _AnglePhi;
  public double AnglePhi { get { return _AnglePhi; } }

  private readonly double _AngleDelta;
  public double AngleDelta { get { return _AngleDelta; } }
  .
  .
}

class SoilCollection
{
  private List<Soil> _Soils;
  public IList<Soil> Soils { get { return _Soils.AsReadOnly(); } }
  .
  .
}

I think the Soil class needed logic inside it, and not inside another class. I 'll post if I find any shortcomings.

You can define Soil as ValueObject then it will be immutable after creation:

class Soil
{
  public Soil(double anglePhi, double angleDelta)
  {
      AnglePhi = anglePhi;
      AngleDelta = angleDelta;
  }

  public double AnglePhi { get; private set; }
  public double AngleDelta { get; private set; }
}

And it's better to rename Soils to SoilCollection I think.

If you want your Soil type to have copy semantics, define it as a struct. You should make it immutable then, by declaring the backing fields as readonly and adding an appropriate constructor.

struct Soil
{
  private readonly double anglePhi;
  private readonly double angleDelta;

  public Soil(double phi, double delta) {
    this.anglePhi = phi;
    this.angleDelta = delta; 
  }

  public double AnglePhi { get { return anglePhi; } }
  public double AngleDelta { get { return angleDelta; } }
}

If you keep it as class, I would not use an indexer to retreive copies of the objects. I'd rather use a method to make clear that the user is getting a copy of the object. And make it read-only, just as the struct above, just as class. That would probably also eliminate the need to make copies.

There's no reason you have to implement both the setter and the getter on the Soil class. You can choose only to implement get which would make the Soil objects readonly.

You'd obviously have to have some other method of setting the internal values - could that be done in a constructor.

For example:

class Soil
{
    private double m_anglePhi;

    public Soil( double anglePhi )
    {
        m_anglePhi = anglePhi;
    }

    public double AnglePhi 
    {
        get { return m_anglePhi; }
    }
}

i would suggest:

1) Make Soil class immutable.

2) Make Soils class a read only collection. As in, derive from IList and declare the add etc methods as explicit implementaion of interfact.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM