The sample code below better explains my issue, its well explained in the comments. Browser contains various implementers of BrowserModule, these modules have a reference to the owner Browser so they can work together internally. Each "main" module has a SpecificModule inside, these "main" modules only serve as a proxy for the specific modules that will implement the target browser automation library like Selenium or Puppeteer etc...
For the BrowserMouse module, it would for example, instantiate the SpecificModule as BrowserPuppeteerChromiumSpecificMouse and delegate the calls to that specific module.
I don't want users of the Browser class to have access to the underlying implementations of each automation library so I hide them that way, but Im having problems as the Mouse position for example can be set by doing Browser.Mouse.PositionOnDocument = new Point(123,123), that ability I would like to be only available internally(namespace level) and not to the end user of the library.
What should be allowed is for example, another module wants to change the position of the Mouse, it could change it normally just accessing Browser.Mouse.PositionOnDocument = new Point();
But that should only be allowed from the same namespace, not from outside use, outside use should be read only.
using System;
using System.Drawing;
namespace Test
{
using InternalNamespace;
class Program
{
public static void Main()
{
Browser browser = new Browser();
browser.Initialize();
Console.WriteLine(browser.Mouse.PositionOnDocument);
// This should NOT be allowed because its being used from outside the Browser namespace
browser.Mouse.PositionOnDocument = new Point(2, 2);
Console.WriteLine(browser.Mouse.PositionOnDocument);
}
}
}
namespace InternalNamespace
{
public class Browser
{
public BrowserMouse Mouse;
public void Initialize()
{
// Instantiate the main mouse with underlying specific puppeteer module
// pass a null module to the specific module because it doesnt need
// one underlying specific module
Mouse = new BrowserMouse(this, new BrowserPuppeteerChromiumSpecificMouse(this, null));
// This is OK because its inside the namespace
Mouse.PositionOnDocument = new Point(1, 1);
}
}
public class BrowserModule
{
// ExternalBrowser that owns this module.
protected Browser Browser { get; set; }
// Underlying specific module => Puppeteer, Selenium or anything.
protected BrowserModule SpecificModule { get; set; }
public BrowserModule(Browser browser, BrowserModule specificModule)
{
Browser = browser;
if (specificModule != null)
SpecificModule = specificModule;
}
}
interface ILocatable
{
Point PositionOnDocument { get; set; }
}
// This whole class just servers as a proxy for the underlying module.
public class BrowserMouse : BrowserModule, ILocatable
{
public BrowserMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
// This delegates the call to the specific module.
public Point PositionOnDocument
{
get
{
return ((ILocatable)SpecificModule).PositionOnDocument;
}
set
{
((ILocatable)SpecificModule).PositionOnDocument = value;
}
}
}
// The specific module implementation that is gonna be called from the main module.
public class BrowserPuppeteerChromiumSpecificMouse : BrowserModule, ILocatable
{
public BrowserPuppeteerChromiumSpecificMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
public Point PositionOnDocument { get; set; }
}
}