问题
I have a library of classes that describe different pieces of connecting hardware such as nails, screws and bolts that we will call the ConnectorLibrary. I am attempting to build a library on top of that one that will handle analyzing the grip capacity of each class in that library that we will call ConnectorGripAnalysisLibrary.
For this question we will work with the classes: Screw
, Bolt
, and Connector
. Both Screw
and Bolt
inherit from Connector
(which is an abstract class) and they are both implemented in the ConnectorLibrary.
There is a different method for determining grip for each class in the base library that I need to implement. So for both Bolt
and Screw
I need to Implement a method such as DoesPassGripTest(Board board)
. (Board being just an example parameter)
If I were going to implement this in the Connectorlibrary, I would put DoesPassGripTest
into Connector
an abstract method and implement the different formulas in the respective derived classes.
The goal is to be able to have my code work like this from the ConnectorGripAnalysisLibrary:
[Test()]
public static void CheckScrewAndBoltGripTest()
{
Board board = new Board();
Bolt b = new Bolt();
Screw s = new Screw();
List<Connector> connectors = new List<Connector>()
connectors.add(b);
connectors.add(s);
foreach(var connector in connectors)
{
if(!connector.DoesPassGripTest(board));
throw new Exception("Grip Test Fails");
}
}
I want to maintain the "Open closed principle" in the ConnectorGripAnalysisLibrary, so that in the event a new Connector is added to the ConnectorLibary, no modification of the ConnectorGripAnalysisLibrary is needed other than adding a new class. "Open for Extension, Closed for Modificaiton"
But how can I build this functionality into the GripAnalysisLibrary that is built on top of the ConnectorLibrary. Is there a slick way that I can do this?
I don't want the ConnectorLibrary to contain the GripAnalysis code and functionality. The ConnectorLibrary is to be opensource while the GripAnalysisLibrary will be proprietary.
回答1:
For doing this as an extension method you need to create a class to define extensions for Connector and include the class wherever you want to use the DoesPassGripTest
method on a Connector instance. The basic outline of the extension is:
public static class ConnectorExtensions
{
public static bool DoesPassGripTest(this Connector connector, Board board)
{
// Some logic to determine which connector is being used
}
}
I don't know if there is any other method for determining which connector is being passed in other than type checking since you didn't provide any details about Connectors or the grip analysis logic. Your extension would need to know how to evaluate the test for a given board for each Connector type you are given.
EDIT:
Based on your desire to have your ConnectorGripAnalysisLibrary be easily extendable and without modification, here is pseudo-code of an approach using reflection that allows you to only need to add a class for each new connector.
Create an interface that supplies information to run the analysis and which type of Connector it is for:
public interface IConnectorGripAnalyzer
{
Type ConnectorType { get; }
bool DoesPassGripTest(Board board);
}
Create a base class using generics for easy implementations of concrete classes:
public class ConnectorGripAnalyzer<T> : IConnectorGripAnalyzer where T : Connector
{
public Type ConnectorType
{
get { return typeof(T); }
}
public virtual bool DoesPassGripTest(Board board)
{
return true;
}
}
Create a repository that can be used to get an IConnectorGripAnalyzer instance by Type using reflection. On first use it gathers all of the various analyzers and stores them by connector Type:
public static class ConnectorAnalyzerRepository
{
private Dictionary<Type, IConnectorGripAnalyzer> connectorGripAnalyzers;
public IConnectorGripAnalyzer GetGripAnalyzer(Connector connector)
{
if (connectorGripAnalyzers == null)
{
connectorGripAnalyzers = new Dictionary<Type, IConnectorGripAnalyzer>();
var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(IConnectorGripAnalyzer).IsAssignableFrom(t));
foreach (var t in types)
{
var c = Activator.CreateInstance(t) as IConnectorGripAnalyzer;
if (c == null)
continue;
connectorGripAnalyzers[c.ConnectorType] = c;
}
}
return connectorGripAnalyzers.ContainsKey(typeof(connector)) ? connectorGripAnalyzers[typeof(connector)] : null;
}
}
The extension for Connector leverages the repository to create an appropriate IConnectorGripAnalyzer instance for the given Connector. In the case that the Type of the Connector has no implementation the example throws an exception, but you could return false or log it as a configuration issue as well:
public static class ConnectorExtensions
{
public static bool DoesPassGripTest(this Connector connector, Board board)
{
var analyzer = ConnectorAnalyzerRepository.GetGripAnalyzer(connector);
if (analyzer == null)
throw new ArgumentException("Invalid connector type"); // Do whatever you want with the failure
return analyzer.DoesPassGripTest(board);
}
}
Adding support for a Connector in the ConnectorGripAnalysisLibrary is now a matter of adding a class that inherits from ConnectorGripAnalyzer with the concrete Connector type. All that is needed is an appropriate override of DoesPassGripTest(Board board)
for the specific Connector:
public class NailConnectorGripAnalyzer : ConnectorGripAnalyzer<NailConnector>
{
public override bool DoesPassGripTest(Board board)
{
return true;
}
}
public class ScrewConnectorGripAnalyzer : ConnectorGripAnalyzer<ScrewConnector>
{
public override bool DoesPassGripTest(Board board)
{
return true;
}
}
来源:https://stackoverflow.com/questions/27724130/simulate-inheritance-by-extension