Skip to main content

Hiding the implementation with accessibility

Object orientation hides the internal implementation of an object and exposes only the operations and attributes that need to be exposed to the outside world. Also, in C#, it is possible to hide classes outside the project. The method of restricting this public range is called accessibility.

AccessibilityVisibility
publicaccessible from outside the project
internalaccessible only within the same project
protectedaccessible only to derived classes
privateaccessible only inside the class

Principles of accessibility

Make accessibility as restrictive as possible to stabilize code quality and localize the impact of design changes.

  • Make classes internal and reduce public classes as much as possible.
  • Methods that are necessary for the internal implementation of the class and should not be accessed from the outside should be private, and methods that should be accessed from inherited classes should be protected.
  • Class variables are private and define properties.
  • Don't have a setter if the property doesn't need to be changed.

Method hiding example

Let's say you have created a service class like this: Since CreateModels(), which is a common process, needs to be called only from within this class, it is hidden with private accessibility.

Example of hiding methods
using NextDesign.Core;

namespace MyExtension.Core
{
/// <summary>
/// create a use case model
/// </summary>
public class UseCaseModelCreationService
{
/// <summary>
/// create an actor
/// </summary>
/// <param name="owner">model that owns the created actor</param>
/// <param name="names">String to set Name of created actor</param>
/// <returns>created actor</returns>
public IEnumerable<IModel> CreateActors(IModel owner, IEnumerable<string> names)
{
var createdModels = CreateModels(owner, "Actors", "Actors", names);
return createdModels;
}

/// <summary>
/// create a use case
/// </summary>
/// <param name="owner">Model that owns the created use case</param>
/// <param name="names">String to set as Name of the created use case</param>
/// <returns>created - use case</returns>
public IEnumerable<IModel> CreateUseCases(IModel owner, IEnumerable<string> names)
{
var createdModels = CreateModels(owner, "UseCases", "UseCase", names);
return createdModels;
}

// Keep the following methods private as they are not called directly from the outside

/// <summary>
/// create the model
/// </summary>
/// <param name="owner">model that owns the created model</param>
/// <param name="fieldName">Field name that owns the created model</param>
/// <param name="className">metaclass name of the model to create</param>
/// <param name="names">String to set as Name of created model</param>
/// <returns>Created model</returns>
private IEnumerable<IModel> CreateModels(IModel owner, string fieldName, string className, IEnumerable<string> names)
{
var createdModels = new List<IModel>();

foreach (var name in names)
{
var model = owner.AddNewModel(fieldName, className);
model.SetField("Name", name);
createdModels.Add(model);
}

return createdModels;
}
}
}

class hiding example

This is an example of sharing logic by grouping it into a class. Since the ModelCreationService class will only be used from within the project, we will set its accessibility to internal.

Example of hiding a class
using NextDesign.Core;

namespace MyExtension.Core
{
/// <summary>
/// create a use case model
/// </summary>
public class UseCaseModelCreationService
{
/// <summary>
/// create an actor
/// </summary>
/// <param name="owner">model that owns the created actor</param>
/// <param name="names">String to set Name of created actor</param>
/// <returns>created actor</returns>
public IEnumerable<IModel> CreateActors(IModel owner, IEnumerable<string> names)
{
var modelCreationService = new ModelCreationService();
var createdModels = modelCreationService.CreateModels(owner, "Actors", "Actor", names);
return createdModels;
}

/// <summary>
/// create a use case
/// </summary>
/// <param name="owner">Model that owns the created use case</param>
/// <param name="names">String to set as Name of the created use case</param>
/// <returns>created - use case</returns>
public IEnumerable<IModel> CreateUseCases(IModel owner, IEnumerable<string> names)
{
var modelCreationService = new ModelCreationService();
var createdModels = modelCreationService.CreateModels(owner, "UseCases", "UseCase", names);
return createdModels;
}
}

// The following classes are not exposed outside the project

/// <summary>
/// create the model
/// </summary>
internal class ModelCreationService
{
/// <summary>
/// create the model
/// </summary>
/// <param name="owner">model that owns the created model</param>
/// <param name="fieldName">Field name that owns the created model</param>
/// <param name="className">metaclass name of the model to create</param>
/// <param name="names">String to set as Name of created model</param>
/// <returns>Created model</returns>
public IEnumerable<IModel> CreateModels(IModel owner, string fieldName, string className, IEnumerable<string> names)
{
var createdModels = new List<IModel>();

foreach (var name in names)
{
var model = owner.AddNewModel(fieldName, className);
model.SetField("Name", name);
createdModels.Add(model);
}

return createdModels;
}
}
}

Hiding implementations with interfaces

By thoroughly hiding the implementation by the interface, the class implementation of the domain layer should be internalized in principle. This makes it possible to realize an architecture that is extremely resistant to design changes. See Hiding the implementation with an interface for details.