C# – Better Options Than Friend Class in C#

Architecturecdesigndesign-patternsobject-oriented

I return to this question every couple of years, so now i decided to solve it once and for all, by asking here.

So, the sequence:

  1. I'm writing a simple application that parses Json file (configuration to 3rd party app, further named as config) and allows user to do some editing with it. I've picked a platform, made some basic UI, binded some buttons to NewFileCommand, OpenFileCommand, SaveFileCommand, etc.
  2. Now I need to store somewhere the opened (and deserialized into a class) config, so I made a new StorageService class. It has some methods like OpenFile(), ReloadFile(), SaveFile(), etc. They are called from the ViewModel, StorageService opens file, read the config and store it as a field. When needed, it saves it to a file – so far so good.
  3. At this moment, I want to add some manipulations with the config itself. I really dont want to put it in the StorageService. Some methods like GetConfigPropertyByNameAndDoSomeMagicWithIt() are definitely should be placed to another service.
  4. So, i made another service, OperationsService. Now i want it it have an access to the config, stored in StorageService and add methods GetConfigPropertyByNameAndDoSomeMagicWithIt(). I dont want to expose the StorageService.config into the public API – because i already have all necessary accessors. For everyone else, except of OperationsService. Which i pretty much want to make the one, who can access StorageService.config. Looking like a good use case for old good C++ concept of the "friend" class, where OperationsService can be declared as a friend of StorageService and access its internals.
  5. I'm recalling that I've faced similar issue earlier, so I googled for "C# friendly class", opened the first link in a hope to find a modern solution – and realized that I've a deja vu feeling.

So, what is a flow in my current approach? How to solve it in "proper modern OOP architecture" manner?
Make OperationsService an internal class of StorageService? That would mean that all the code would be stored in the same file (pretty much violation of SOLID).
Make OperationsService an internal class of StorageService and make it partial and move to another file? Seems kinda creepy.

In general, I don't even want OperationsService to be exposed (not even to register it in IoC container) for the rest of the app – just to do the heavy lifting. OperationsService would be not that large, so I don't want to make a third (some kind of a wrapper service) as well.

In terms of objects I can rephrase the problem as "object A has a private field, which should be accessible only for object B. Object's A responsibility is IO, Object's B responsibility is some logic, so they cant be merged".

To sum up, we have:

  • config, which is POCO and contains deserialized data from Json.
  • StorageService, which contains OpenFile(), SaveFile(). Also, it contains current instance of the config.
  • theoretical OperationsService, which contains some logic in GetConfigPropertyByNameAndDoSomeMagicWithIt(). In order to run that logic, it has to have an access to the config as well. The question is how to relate OperationsService and StorageService.

Best Answer

One way of priveliging OperationsService over the rest of the system in regards to specific data is directly passing such data to it on a need to use basis: You could keep your config manipulation logic inside OperationsService and add a public AcceptOperations() method in StorageService that takes an OperationsService object as an arg and calls upon its relevant config manipulation logic, directly passing to it any privately held relevant config data.

Concretely, you could still have GetConfigPropertyByNameAndDoSomeMagicWithIt() be a member of OperationsService, that would call AcceptOperations() on the relevant StorageService instance and pass this as an arg. AcceptOperations() itself would call some concrete ManipulateConfig() method on the OperationsService instance given to it as a param, passing the internal config data to be manipulated as args for ManipulateConfig().

This proposal takes in part some resemblance to the classic OOP Visitor Pattern, excluding the Double Dispatch idiom but keeping the concept of restrictively defining who can visit what and definitely in keeping with the original idea behind the pattern which is to perform such separation of responsibilities such that "... [it provides] the ability to add new operations to existent object structures without modifying the structures." The whole idea of such a pattern is to aid follow the Open-closed principle which is one of SOLID principles, keeping of which you have mentioned to be a concern of yours to begin with.

Related Topic