Using a common interface in an interface

Update:

Heinzi is right. AutoCAD Polyline is a reference type, not a structure. Good point. But I simplified the script, because what I'm dealing with in a real application is an AutoCAD object, which is a structure. Therefore, please consider it as a structured, non-reference type.


I am looking for the right approach to accept this situation, and would appreciate if anyone could shed some light or help me better understand.

There is an interface at the data access level with two implementations for working with two different providers: AutoCad API and Sketchup.

interface IEntity { void object GetPoly(); void void InsertPoly(object poly); } class AutocadEntity { void object GetPoly() { //calling Autocad APIs return Autocad Polyline object } void InsertPoly(object poly){...} } 

The implementation of Autocad GetPoly will return a Polyline object, as it is defined in the Autocad APIs as a polyline, while Sketchup will return a Face object.

I defined the return type (and parameter) as an object to work with these different types. Cost is a performance issue in which boxing / unpacking occurs. And it more boldly shows where return / parameter is the object [].

At first I wondered if the return / parameter type generic method is a solution, but then I think this is not the case, since implementations are type-specific.

+5
source share
3 answers

Try using an adapter template to adapt the PolyLine and Face types to the same type you would like to work with. For instance:

 public abstract class BasePoly { public abstract double X { get; set; } public abstract double Y { get; set; } public abstract double Width { get; set; } public abstract double Height { get; set; } } public abstract class BasePoly<T> : BasePoly { public T poly { get; private set; } protected BasePoly(T poly) { this.poly = poly; } } public class PolyLineAdapter : BasePoly<PolyLine> { public PolyLineAdapter(PolyLine poly) : base(poly) {} // override abstracts and forward to inner PolyLine instance at 'this.poly' public override double X { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Y { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Width { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Height { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } } public class FaceAdapter : BasePoly<Face> { public FaceAdapter(Face poly) : base(poly) {} // override abstracts and forward to inner Face instance at 'this.poly' public override double X { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Y { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Width { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override double Height { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } } interface IEntity { BasePoly GetPoly(); void InsertPoly(BasePoly poly); } public abstract class Entity<TEntity> : IEntity where TEntity : BasePoly { public BasePoly GetPoly() { return this.GetExternalPoly(); } public abstract TEntity GetExternalPoly(); public void InsertPoly(BasePoly poly) { this.InsertExternalPoly((TEntity) poly); } public abstract void InsertExternalPoly(TEntity poly); } public class AutocadEntity : Entity<PolyLineAdapter> { public override PolyLineAdapter GetExternalPoly() { throw new NotImplementedException(); } public override void InsertExternalPoly(PolyLineAdapter poly) { throw new NotImplementedException(); } } public class SketchupEntity : Entity<FaceAdapter> { public override FaceAdapter GetExternalPoly() { throw new NotImplementedException(); } public override void InsertExternalPoly(FaceAdapter poly) { throw new NotImplementedException(); } } // fills for third party classes public class PolyLine {} public class Face {} 

Using the adapter template , you provide a proxy layer to match the two types of third parties to the type you want to work with.

Keep in mind that this design assumes that you will only work with one type of third-party engine at a time. If you work with both engines at the same time, make the following change:

 public class BasePoly { public double X { get; set; } public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } } interface IEntity { BasePoly GetPoly(); void InsertPoly(BasePoly poly); } public abstract class Entity : IEntity { public abstract BasePoly GetPoly(); public abstract void InsertPoly(BasePoly poly); } public class AutocadEntity : Entity { public override BasePoly GetPoly() { // retrieve external type, convert it to BasePoly and return that throw new NotImplementedException(); } public override void InsertPoly(BasePoly poly) { // convert BasePoly to external type and insert that throw new NotImplementedException(); } } public class SketchupEntity : Entity { public override BasePoly GetPoly() { // retrieve external type, convert it to BasePoly and return that throw new NotImplementedException(); } public override void InsertPoly(BasePoly poly) { // convert BasePoly to external type and insert that throw new NotImplementedException(); } } // fills for third party classes public class PolyLine {} public class Face {} 

In addition, if you are worried about the cost of the box or conversion operations of the adapter (which I would not have until you actually measured and determined whether you need to optimize it), you can use the adapter template for callers who consume IEntity instead of themselves PolyLineAdapter / FaceAdapter or AutocadEntity / SketchupEntity types. Essentially create a plugin engine. You can use Generics to abstract common idioms between two implementations.

Here is a dotnetfiddle example: https://dotnetfiddle.net/UsFPM7

+1
source

Cost is a performance issue in which boxing / unpacking occurs.

I do not think so. Polyline is a class, not a structure. Thus, boxing is not involved.

If this is actually the performance bottleneck of your application, your performance is lost somewhere else. As always: measure before optimizing, or you may end up optimizing the wrong things.

I think your solution is excellent. You can use generics and derive AutocadEntity from IEntity<Polyline> , but what's the point? Since Polyline / Face is used as an input and output parameter in your interface, you can make IEntity non-invariant. Thus, the most common base types of IEntity<Polyline> and IEntity<Face> will be object , which means you can no longer just pass in a generic IEntity if you don't know the specific type.

+4
source

Since you have two different classes implementing the interface, I would think that it is best to make a common interface.

 interface IEntity<T> { T GetPoly(); void InsertPoly(T poly); } class AutocadEntity : IEntity<Polyline> { Polyline GetPoly(){...} void InsertPoly(Polyline poly) {...} } 
+1
source

All Articles