Encapsulate your Fields at object level

There is one interesting
detail of modern OOP language (C#, Java, VB.NET, C++…) that often developers
are not aware of: the ‘private encapsulation’ applies at class level and not at
object level. For example, in the following program, we can see that the object
foo1 is able to change the private
state of the object foo2.

class Foo
{

   private int m_Field = 0;

   internal void UpdateField(Foo
foo) {

      foo.m_Field = 1;

   }

   static void Main() {

      Foo
foo1 = new Foo();

      Foo
foo2 = new Foo();

      foo1.UpdateField(foo2);

   }

}

To avoid potential
corrupted state, we often want to encapsulate our fields at object level.
Thanks to the IsDirectlyWritingField
CQL condition, you can restrict the writing access to m_Field to a single property setter. By using the NDepend.CQL.CQLConstraint attribute
(found in $NDepend Install Dir$\Lib\NDepend.CQL.dll),
you can tag the declaration of m_Field
directly with such CQL constraint. Then, our example can be rewritten like this:

using NDepend.CQL;

 

class Foo
{

   [CQLConstraint(@"//
<Name>Encapsulate m_Field state at object level</Name>

WARN IF Count > 0 IN SELECT METHODS WHERE

IsDirectlyWritingField ""Foo.m_Field""

AND !FullNameIs ""Foo.set_Field(Int32)""
"
)]

   private int m_Field = 0;

   private int Field { set {
m_Field = value; } }

   internal void UpdateField(Foo
foo) {

      foo.Field = 1;

      // ‘foo.m_Field
= 1;’ would provoke a CQL constraint warning.

   }

   static void Main() {

      Foo
foo1 = new Foo();

      Foo
foo2 = new Foo();

      foo1.UpdateField(foo2);

   }

}

This way, we will be
automatically warned by the CQL constraint as soon as another method than the Field property setter assigns directly m_Field.

 

Update: Jason asked below why this is useful to encapsulate the access to a field. I admit that I find this possibility so useful that I didn’t take the time to explain the ‘why’ properly.

A very common need is to be able to track at debugging time all writing access to a field. This way you can figure out why and when it is assigned to the invalid state you noticed. By encapsulating your field this way, you just have to put a single breakpoint to achieve this (instead of having to put N breakpoints at th N places where your field get assigned).  Unfortunatly, the debugger don’t provide such facility yet (I remember that I have read somewhere that it is a feature hard to provide because of all underlying CLR optimizations).

It can also be useful to intercept all assignement to your field to do something special, such as logging, transforming the value, counting the number of writing etc…

 

 

A very common need for object encapsulation (not handled by the
VisualStudio debugger), is to be able to track at debugging time all
writing access to a field because you figured out that it ends up with
an invalid state.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.NDepend.com Patrick Smacchia

    Your idea of
    “IsDirectlyWritingField ::referencedField”
    sounds good, we take note.

    CQL queries and constraints can be written both in the code or in a separate NDepend project.

    Personnally, I like to have CQL queries inside my code to make
    the underlying intention explicit while reading at the code. I don’t have to have the bodies of my method cluttered though, but this is not the case here.

  • http://community.hdri.net/blogs/chads_blog cmyers

    Also, what is the reasoning for having something like CQLConstraint actually in the code? Maybe I’m a purist, but I hate to have things that aren’t directly related to the functioning of the code cluttering up the visual flow of the code.

    It seems that CQLConstraints would be better defined in an external file and run as a separate build step (like unit tests, FxCop, etc)

  • http://community.hdri.net/blogs/chads_blog cmyers

    Without knowing a lot about NDepend and CQL, a neat feature might be to have the CQL constraint language be able to reference the attributed element.

    So instead of having an attribute on m_Field and then again having to reference “m_Field” in the CQL, why not have something like:

    “IsDirectlyWritingField ::referencedField”

    That way you still retain renaming ability without sacrificing constraints.

  • http://www.NDepend.com Patrick Smacchia

    You need this everytime you want to ensure a stronger encapsulation of a field.

    A very common need for object encapsulation (not handled by the VisualStudio debugger), is to be able to track at debugging time all writing access to a field because you figured out that it ends up with an invalid state.

    You can also imagine that you wish to do something each time you access the field, such as a transformation of the value or counting the number of access, or logging etc…

    If the field get disconnected, the NDepend compiler cannot retrieve it and emit an error. So yes, you get warned if the field name get out of sync.

  • http://www.lostechies.com/blogs/jason_meridth Jason Meridth

    What prompted this? When would you ever need this?

    Also you’ve lost ReSharper rename power. The IsDirectlyWritingField item takes a string, so if you change the name with ReSharper (or other tools) it will be disconnected. Also same problem with the type.

    Do you get a compile-time warning/error if the attribute is disconnected from the property in any way?