loading...

Why all our objects be a little more private

jeastham1993 profile image James Eastham ・3 min read

When I first started developing software, and for a good period of my development life, I didn't care or really know about object property accessors.

Every class I created would look like this

public class FileMapper
{
    public string FileName { get; set; }
    public string FileType { get; set; }
    public string FullFilePath { get; set; }
    public string UniqueIdentifier { get; set; }
}

I would use accessors for methods and the classes themselves, but I never really cared about the properties.

This created some rather interesting debugging sessions.

One of the blessings and curses of the above class, is that ANY one of the properties can be set from ANY place in your codebase.

I'll just let that sink in.

The properties of an object can be changed from absolutely anywhere.

If you are the one and only person working on the code base, this may not cause too many headaches. You yourself know that the UniqueIdentifier should only ever be set once and never changed. And that the FullFilePath is made up of a base directory + the unique identifier + the file name.

For every file that has been mapped previously, this structure stands. For other services that may rely on this same structure, a change would cause missing files here, there and everywhere.

However, once you stop working on the code base and a new developer starts they do not have the same knowledge.

They may start manipulating the file path after the file has already been saved.

They may even change the unique identifier, causing the once built file path to now be completely incorrect.

Private setters to the rescue

So let's add some private setters to the class.

public class FileMapper
{
    public FileMapper()
    {
        this.UniqueIdentifier = Guid.NewGuid().ToString();
        this.FullFilePath = Path.Combine(Constants.SomeDirectoryPath, this.UniqueIdentifier, this.FileName);
    }

    public string FileName { get; private set; }
    public string FileType { get; private set; }
    public string FullFilePath { get; private set; }
    public string UniqueIdentifier { get; private set; }
}

I want to state now, one of the most important concepts I stick by when writing code.

The only thing that should be able to update the state of an object, is the object itself

Whilst some people might think that's overkill and a little impractical, it certainly saves a lot of headaches.

I know, for an absolute fact, that once the FullFilePath has been set, it will never be changed.

Properties do change, and that's where methods come in. To update the file path of a FileMapper, use a method not the property directly. That allows code like this

public class FileMapper
{
    public FileMapper()
    {
        this.UniqueIdentifier = Guid.NewGuid().ToString();
        this.FullFilePath = Path.Combine(Constants.SomeDirectoryPath, this.UniqueIdentifier, this.FileName);
    }

    public string FileName { get; private set; }
    public string FileType { get; private set; }
    public string FullFilePath { get; private set; }
    public string UniqueIdentifier { get; private set; }

    public void UpdateFilePath(string newBasePath)
    {
        foreach (var file in Directory.GetFiles(Path.Combine(Constants.SomeDirectoryPath, this.UniqueIdentifier))
        {
            File.Move(file, Path.Combine(newBasePath, this.UniqueIdentifier, Path.GetFileName(file));
        }

        this.FullFilePath = Path.Combine(newBasePath, this.UniqueIdentifier, this.FileName);
    }
}

I know that whenever the file path is updated, all the previous files will come along with it. No file loss, no missing data.

That method can be called whenever, by whoever, and the same things will happen.

If a new developer comes along and wants to work out what is going on, it's explicit and easy to follow.

I'd love to hear your thoughts on the usage of private setters, but it is certainly one of my core concepts in development.

Remember

The only thing that should be able to update the state of an object, is the object itself

Discussion

pic
Editor guide
Collapse
peledzohar profile image
Zohar Peled

Private setters are good, but they are still only a partial fix. c#6 introduced readonly auto-implemented properties - where the set is omitted the property is truly readonly - the c# compiler creates a readonly backing field for that property, so you can only set it's value inline or inside one of your type's constructors - while with a private setter you can still change the property's value from anywhere inside your type.

There are use-cases for both immutable and readonly properties, and even for public setters (though admittedly, I totally agree that these should be only used with caution and after careful consideration), though IMHO, A good design should strive for immutability in most cases.

Collapse
jeastham1993 profile image
James Eastham Author

Ahhh, I didn't realize c# 6 introduced read only for all properties. I've used read only collections pretty extensively but never for other types.

Yeah, in some cases there is a use case for non private setters. But I can think of almost zero situations where a private one wouldn't give more robust and easy to amend code.

Collapse
chamiell profile image
Chamiell

Soo, why not use the setter to do the other needed stuff?

Collapse
jeastham1993 profile image
James Eastham Author

Great question! And in some instances that's what I would do.

However, it's not very explicit. Setting a property and then a load of stuff happening behind the scenes isn't good for other devs reading the code.

Looking back at my sample code, the method should probably be called MoveFiles instead of UpdateIdentifiwr