I need to extend a class where the child class will not use some members of the parent class. How should the unused members be handled and still respect the open/closed SOLID principle? I'm considering doing something like the code below, but existing code that uses reflection will get exceptions. If I put data in the deprecated members, existing code will produce unexpected results.
I feel that a totally different approach should probably be used. How to handle this situation without modifying existing code (which would also violate the open/closed principle)?
class Parent
{
public virtual int myVarMin { get; set; } = 0;
public virtual int myVarMax { get; set; } = 10;
public int myVar { get; set; }
public int unchanged1 {get; set;}
//.
//numerous other members that are irrelevant to the question
//.
public void doSomething(){/*do something*/}
//
}
class Child:Parent
{
//considered impementation from original question
public override int myVarMin => throw new NotSupportedException();
public override int myVarMax => throw new NotSupportedException();
public List<int> myVarList = new List<int>();
}
class MyExistingCode
{
public void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
}
}
}
Use the Obsolete attribute to inform the developers that your method is deprecated and they should use the new version.
[Obsolete("Method is deprecated, use Method2 please.")]
public void Method()
{
…
}
Here I've changed your code, so using reflection you can detect whether a method/property is deprecated or not, and it'll not throw an exception anymore.
public class Parent
{
public virtual int myVarMin { get; set; } = 0;
public virtual int myVarMax { get; set; } = 10;
public int myVar { get; set; }
}
public class Child : Parent
{
[Obsolete("Use other property")]
public override int myVarMin => throw new NotSupportedException();
[Obsolete("Use other property")]
public override int myVarMax => throw new NotSupportedException();
public List<int> myVarList = new List<int>();
}
class MyExistingCode
{
public void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
var customeAttributes = (ObsoleteAttribute[])info.GetCustomAttributes(typeof(ObsoleteAttribute), false);
if (customeAttributes.Length > 0)
{
Console.WriteLine($"{info.Name} is deprecated.");
}
else
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
}
}
}
}
I ended up doing something like this (the Parent and MyExistingCode classes were unchanged, so they comply with the open/closed principle):
class Child : Parent
{
public new int? myVarMin => null;
public new int? myVarMax => null;
public List<int> myVarList = new List<int>();
}
class MyNewCode : MyExistingCode
{
public new void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent) ?? "NULL"}");
}
}
}
I was surprised that I could hide an int with an int? without an error.
I will Accept another answer if it is better.
Related
there is a concept about inheritance which I do not quite understand.
I have a
protected DeveloperReport DeveloperReport; // Field
Wouldn't PersonalInfoBuilder be able to access that field ?
If yes,
public PersonalInfoBuilder MyPersonalInfo => new PersonalInfoBuilder(DeveloperReport);
Why do I still have to pass the DeveloperReport(field) into PersonalInfoBuilder constructor, when I can
just modify the protected DeveloperReport field by calling new PersonalInfoBuilder(), instead of
new PersonalInfoBuilder(DeveloperReport)?
And, how the concept of "return this" return the changes made to DeveloperReport(field) back to
DeveloperReportBuilder?
Thanks !
class DeveloperReport
{
// Properties
public int Id { get; set; }
public string Name { get; set; }
public DeveloperLevel Level { get; set; }
public int WorkingHours { get; set; }
public int HourlyRate { get; set; }
// Methods
public double CalculateSalary() => WorkingHours * HourlyRate;
}
class DeveloperReportBuilder
{
protected DeveloperReport DeveloperReport;
public PersonalInfoBuilder MyPersonalInfo => new PersonalInfoBuilder(DeveloperReport);
public DeveloperReportBuilder()
{
DeveloperReport = new DeveloperReport();
}
// return developer report.
public DeveloperReport Build() => DeveloperReport;
}
class PersonalInfoBuilder : DeveloperReportBuilder
{
public PersonalInfoBuilder(DeveloperReport report)
{
DeveloperReport = report;
}
public PersonalInfoBuilder()
{
}
public PersonalInfoBuilder NameIs(string name)
{
DeveloperReport.Name = name;
return this;
}
public PersonalInfoBuilder IDis(int id)
{
DeveloperReport.Id = id;
return this;
}
}
You only have to pass the report instance if you want to have both instances of DeveloperReportBuilder and PersonalInfoBuilder have acces to the same instance of DeveloperReport.
Inheritance will not copy the instance values.
Code sample to begin with:
internal class ClubHouse : ILeasable
{
public int Id { get; set; }
public int AreaInSquareFeet { get; set; }
}
public class Parking : ILeasable
{
public int Id { get; set; }
public int CarCapacity { get; set; }
}
internal interface ILeasable
{
int Id { get; set; }
}
class LeasableRepository
{
private List<ILeasable> _leasable = new List<ILeasable>()
{
new ClubHouse() {Id = 208, AreaInSquareFeet = 7500 },
new ShowRoom(){ Id = 202, AreaInSquareFeet = 4000 },
new Parking() {Id = 504, CarCapacity = 4},
};
private Dictionary<int, ILeasable> _leasableDictionary = new Dictionary<int, ILeasable>();
public LeasableRepository()
{
_leasableDictionary = _leasable.ToDictionary(x => x.Id, x => x);
}
public ILeasable GetLeasable(int id)
{
if (_leasableDictionary.ContainsKey(id)) return _leasableDictionary[id];
return null;
}
}
public class ChargeCalculatingFacade
{
LeasableRepository leasableRepository = new LeasableRepository();
public void ShowLeasingCharges(int id)
{
var leasable = leasableRepository.GetLeasable(id);
var leasingCharge = GetLeasingCharges(leasable);
}
private int GetLeasingCharges(ILeasable leasable)
{
// This is not possible as I can't be sure that leasable is ClubHouse
var property = (ClubHouse) leasable;
var areaInSquareFeet = property.AreaInSquareFeet;
return areaInSquareFeet * 10;
}
}
Now, in class ChargeCalculatingFacade class, in method ShowLeasingCharges(int id), based on the id, I called GetLeasable(int id) which returns one of the implementation of ILeasable. However it return as an interface ILeasable.
I pass that ILeasable to a private method GetLeasingCharges(leasable) to calculate the leasing charges based on the AreaInSquareFeet.
Now, leasable parameter is just ILeasable, which has just "Id" property available. Now how to identify which concreat class implementation is passed as parameter, I can cast it to get AreaInSquareFeet like this
var property = (ClubHouse) leasable;
var areaInSquareFeet = property.AreaInSquareFeet;
But the above code is not posible as I am not sure if the leasable is ClubHouse as it just picks leasable from a dictionary based on Id.
All class does not have the same additional property. For instance, Parking has additional property as "CarCapacity". I have 10 such classes, now cannot put 10 if logic to check if the interface is of required class type.
I wonder if some design pattern or some SOLID principle can simplify the design.
I have following questions:
How do I get the areaInSquareFeet in such case
Is this a good practice to have an interface with few methods and properties and again have additional public methods or properties in concreate class.
Note: I do not want to use reflection. I would like to change a design in case without reflection is not possible. Any design suggestions? Any desing pattern can be used in such scenario?'
Thank you.
Mita
A. ILeasable.GetLeasingCharges
If GetLeasingCharges depends only on the data the object already has I could be argued that it may be better choice to make GetLeasingCharges part of ILeasable.
internal interface ILeasable
{
int Id { get; set; }
int GetLeasingCharges();
}
internal class ClubHouse : ILeasable
{
public int Id { get; set; }
public int AreaInSquareFeet { get; set; }
public int GetLeasingCharges() => AreaInSquareFeet * 10;
}
internal class ClubHouse : ILeasable
{
public int Id { get; set; }
public int CarCapcity{ get; set; }
public int GetLeasingCharges() => CarCapcity * 15;
}
B. GetLeasingCharges not part ILeasable
From C#7.0 you can use pattern matching for situations like this.
public static int GetLeasingCharges(ILeasable leasable)
{
// From c#7.0
switch (leasable)
{
case ClubHouse c:
return c.AreaInSquareFeet * 10;
case ShowRoom s:
return s.AreaInSquareFeet * 12;
case Parking p:
throw new ArgumentException(
message: "Parkings cannot be leased!",
paramName: nameof(leasable));
default:
throw new ArgumentException(
message: "Unknown type",
paramName: nameof(leasable));
}
}
Before C#7.0 you could use if.
if (leasable is ClubHouse)
{
var c = (ClubHouse)leasable;
return c.AreaInSquareFeet * 10;
}
else if (leasable is ShowRoom)
{
var c = (ShowRoom)leasable;
return s.AreaInSquareFeet * 12;
}
else if(leasable is Parking)
{
throw new ArgumentException(
message: "Parkings cannot be leased!",
paramName: nameof(leasable));
}
else
{
throw new ArgumentException(
message: "Unknown type",
paramName: nameof(leasable));
}
I quite agree with #tymtam approach. You can also use an abstract class in an alternative.
public abstract class ChargeCalculatingFacadeBase<T> where T : ILeasable
{
LeasableRepository leasableRepository = new LeasableRepository();
public ILeasable leasable;
public void ShowLeasingCharges(int id)
{
leasable = leasableRepository.GetLeasable(id);
var leasingCharge = GetLeasingCharges((T)leasable);
}
public abstract int GetLeasingCharges(T leasable);
}
public class ChargeCalculatingFacade : ChargeCalculatingFacadeBase<ClubHouse>
{
public override int GetLeasingCharges(ClubHouse leasable)
{
var property = leasable;
var areaInSquareFeet = property.AreaInSquareFeet;
return areaInSquareFeet * 10;
}
}
While this might be an overkill, in a case similar to yours I have on a few occasions used a pseudo-DSL approach.
That is, I first come up with a language to express my intent, and then implement it.
What do you need? The ability to express calculations in a readable way. Let's do it this way: assuming you have a class
public class LeaseCalculator
{
public int CalculateLease(int id) ...
I'd like to initialize it like this:
var builder = new LeaseCalculatorBuilder();
LeaseCalculator calculator = builder
.On<ClubHouse>(house => house.AreaInSquareFeet)
.On<Parking>(park => park.CarCapacity)
.On<ShowRoom>(room => room.AreaInSquareFeet)
.Build(leasableRepository);
Is the intent clear? I believe so. If we have a club house, we do the first thing; for parking, something else, etc, etc.
Now, to the implementation. I could also walk step by step, but to cut story short:
public class LeaseCalculatorBuilder
{
internal Dictionary<Type, Func<ILeasable, int>> Calculations { get; } = new Dictionary<Type, Func<ILeasable, int>>();
internal LeaseCalculatorBuilder On<T>(Func<T, int> calculation) where T : class, ILeasable
{
Calculations.Add(typeof(T), (ILeasable c) => calculation((T)c));
return this;
}
internal LeaseCalculator Build(LeasableRepository leasableRepository)
{
return new LeaseCalculator(leasableRepository, this);
}
}
public class LeaseCalculator
{
private readonly Dictionary<Type, Func<ILeasable, int>> _calculations;
private readonly LeasableRepository _leasableRepository;
internal LeaseCalculator(LeasableRepository leasableRepository, LeaseCalculatorBuilder builder)
{
_leasableRepository = leasableRepository;
_calculations = builder.Calculations;
}
public int CalculateLease(int id)
{
ILeasable property = _leasableRepository.GetLeasable(id);
Type type = property.GetType();
if (_calculations.TryGetValue(type, out var calculation))
{
return calculation(property);
}
throw new Exception("Unexpected type, please extend the calculator");
}
}
And finally, a default creator:
public static class DefaultLeaseCalculator
{
internal static LeaseCalculator Build(LeasableRepository leasableRepository)
{
var builder = new LeaseCalculatorBuilder();
LeaseCalculator calculator = builder
.On<ClubHouse>(house => house.AreaInSquareFeet)
.On<Parking>(park => park.CarCapacity)
.On<ShowRoom>(room => room.AreaInSquareFeet)
.Build(leasableRepository);
return calculator;
}
}
Neat?
I have the following code:
public interface BaseInterface
{
int ID { get; }
}
public interface SpecialInterface1 : BaseInterface
{
int price { get; }
}
public interface SpecialInterface1 : BaseInterface
{
int xyz { get; }
}
public class Implementation1 : SpecialInterface
{
int price { get; }
int ID { get; internal set; }
}
public class Implementation2 : SpecialInterface
{
int xyz { get; }
int ID { get; internal set; }
}
Now in a Management class I want to add the objects that implement BaseInterface into a List.
I know that I can use as or is to cast the interface to an implementation, but in my project, I have about 10 special interfaces with an implementation each so I would have to write a really big if statements.
public void Add(BaseInterface u, int id)
{
if (u is Implementation1)
{
((Implementation1)u).ID = id;
Units.Add(u);
}
if (u is Implementation2)
{
((Implementation2)u).ID = id;
Units.Add(u);
}
}
My goal is that the id is not changeable outside the implementation and I would provide only the interfaces outside my dll so none can change the id.
A solution would be to add an extra interface. This eliminates the internal setter in your implementation.
internal interface IChangeID
{
void SetID(int id);
}
public interface IBaseInterface
{
int ID { get; }
}
public class Implementation : IBaseInterface,
IChangeID
{
public void SetID(int id) { ID = id; }
public int ID { get; private set; }
}
Only the real implementations should implement IChangeID. Returning IBaseInterface or ISpecialInterface will hide the setter, because those interfaces do not inherit from IChangeID.
This would change your add into:
public void Add(BaseInterface u, int id)
{
((IChangeID)u).SetID(id);
Units.Add(u);
}
If you do want to return the concrete types, not interfaces. You could implement the given interface explicit. This will hide the set method even from the concrete implementation.
public class Implementation : IBaseInterface,
IChangeID
{
void IChangeID.SetID(int id) { ID = id; }
public int ID { get; private set; }
}
var obj = new Implementation();
obj.SetID() // This WILL NOT Compile
If you don't want to modify the interfaces and implementations, you could use C# 7's pattern matching to access the implementation type without casting. It requires 3 lines per implementation type but avoids modifying the classes:
public void Add(BaseInterface u, int id)
{
switch(u)
{
case Implementation1 u1:
u1.ID = id;
break;
case Implementation2 u1:
u1.ID = id;
break;
default :
throw new ArgumentException("Unexpected implementation!");
}
Units.Add(u);
}
The obvious disadvantage is that the code will have to be modified if a new implementation is added.
Another option is to use dynamic, losing type safety. This will fail at runtime if some implementation doesn't have a setter (eg because it was replaced by constructor initialization)
public void Add(BaseInterface u, int id)
{
dynamic x =u;
x.ID=id;
Units.Add(x);
}
While I like this answer the best,
I recommend making the ID a required parameter of all the implementation's constructors, and then to use a factory pattern to generate any instance you require. This makes any instance without the ID set throw an exception at compile time rather than runtime reducing the probability of exceptions.
Here is a simple example that gets you what you want without an additional interface. Should you choose you can combine my answer with #Iqon's answer.
public interface IInterface
{
int ID { get; }
}
internal class InternalImplementation: IInterface {
public InternalImplementation(int ID) { this.ID = ID; }
public int ID { get; set; }
}
public class MyImplementationFactoryService {
public IInterface Create() {
int id = 1 // Or however you get your ID, possibly from a DB query?
return new InternalImplementation(id);
}
public IInterface Create(type|enum createtype) {
// return type based on typeof or enum
}
}
In case you want to use reflection to set property, code below may help
public interface IBaseInterface
{
int ID { get; }
}
public class Impl1 : IBaseInterface
{
public int ID { get; internal set; }
public int Price {get; set;}
}
public class Impl2 : IBaseInterface
{
public int ID { get { return 0;} }
public int Subscription {get; set;}
}
public class Program
{
public static void Main(string[] args)
{
IBaseInterface obj1 = new Impl1();
SetProperty(obj1, "ID", 100);
Console.WriteLine("Object1 Id is {0}", obj1.ID);
IBaseInterface obj2 = new Impl2();
SetProperty(obj2, "ID", 500);
Console.WriteLine("Object2 Id is {0}", obj2.ID);
}
private static void SetProperty(IBaseInterface obj, string propertyName, object id){
if(obj.GetType().GetProperty(propertyName).CanWrite) {
obj.GetType().GetProperty(propertyName).SetValue(obj, id);
Console.WriteLine("CanWrite property '{0}' : {1}" , propertyName, obj.GetType().GetProperty(propertyName).CanWrite);
}
}
}
Output
CanWrite property 'ID' : True
Object1 Id is 100
Object2 Id is 0
I want to have class which will have an indexer and number of fields like in the following example:
public abstract class ARecord
{
public abstract double this[int index] { get; }
public abstract int NumberOfFields { get; }
}
public class Record : ARecord
{
public double Field1{ get; private set; }
public double Field2{ get; private set; }
public override int NumberOfFields { get { return 2; } }
public Record(double[] records)
{
if (records.Count() != NumberOfFields) // PROBLEM IS HERE. WHEN CALLING THIS FROM DERIVED CLASS NumberOfFields=3!
throw new ArgumentException();
this.Field1= records[0];
this.Field2 = records[1];
}
public override double this[int index]
{
get { throw new NotImplementedException(); }
}
}
public class ChildRecord : Record
{
public double Field3 { get; private set; }
public override int NumberOfFields { get { return 3; } }
public ChildRecord(double[] records)
: base(new double[] { records[0], records[1] })
{
if (records.Count() != NumberOfFields)
throw new ArgumentException();
this.Field3 = records[2];
}
public override double this[int index]
{
get { throw new NotImplementedException(); }
}
}
public static class TestRecord
{
public static void CreateRecord()
{
var record = new ChildRecord(new double[]{1.0,1.5,2.5}); // Not working
}
}
This example crashes because of polymorphic call NumberOfFields from ChildRecord inside constructor of Record.
As far as I know, I can use new insted of override to solve this problem, but in that case I cannot declare NumberOfFields as abstract in base class (which I needed).
What is the proper way to solve this problem? Is something wrong with the design?
The way you've formulated this, it cannot possibly work as intended. Assume it did (through some sort of some magic) work the way you think it should, and you were able to create your ChildRecord:
var record = new ChildRecord(new double[] { 1.0, 1.5, 2.5 });
What would you expect the value of record.NumberOfFields to be? Two or three? This particular object cannot be a ChildRecord with NumberOfFields == 3 and at the same time be a Record with NumberOfFields == 2. You get the result of the NumberOfFields implementation of the instantiated class, regardless of whether you type record as ARecord, Record or ChildRecord.
To put it another way: It makes no sense to expose ARecord.NumberOfFields to the outside, as there is no one correct answer -- it isn't a meaningful concept.
If you abstain from exposing it, you can do your validation something like this (with indexers and properties omitted):
public abstract class ARecord
{
public abstract double this[int index] { get; }
}
public class Record : ARecord
{
private const int NumberOfFields = 2;
public Record(double[] records)
{
if (records.Count() != NumberOfFields)
throw new ArgumentException();
this.Field1 = records[0];
this.Field2 = records[1];
}
}
public class ChildRecord : Record
{
private const int NumberOfFields = 3;
public ChildRecord(double[] records)
: base(new double[] { records[0], records[1] })
{
if (records.Count() != NumberOfFields)
throw new ArgumentException();
this.Field3 = records[2];
}
}
I'm having a hard trying to find to correct approach to this :
My data structures :
public abstract class Flow
{
public virtual double Value { get; set; }
public virtual DateTime Time { get; set; }
}
public class InboundFlow : Flow
{
}
public class OutboundFlow : Flow
{
}
My business objects containing collections of these data structures
public abstract class Fluent
{
public virtual IList<Flow> FlowCollection { get; set; }
public virtual double InitialBaseflow { get; set; }
}
public class Affluent : Fluent
{
public new virtual IList<InboundFlow> FlowCollection { get; set; }
}
public class Effluent : Fluent
{
public new virtual IList<OutboundFlow> FlowCollection { get; set; }
}
The generic method I'm trying to use :
private static void FindInitialBaseflow<T>(ref T fluent) where T : Fluent
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.ToList().FindAll(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
My problem is that calling "linqfluent.FlowCollection" in the linq method calls for the base class Fluent's FlowCollection, which is null.
How can I force the use of the child's property instead? Thanks!
You need to make the collection within Fluent generic so that the classes that inherit from it can specify the type:
public class Fluent<T>
where T : Flow
{
public IList<T> FlowCollection { get; set; }
public double InitialBaseflow { get; set; }
}
Once you have that you don't even need sub classes of Flow, you can just make it concrete.
Your use of it would be easily modified to fit this model:
private static void FindInitialBaseflow<T>(Fluent<T> fluent)
where T : Flow
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.Where(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
Also note that since you're not setting fluent in this method, there is no need to pass it by reference. It's already a class, so it is itself a reference; mutations of the referenced object will be observed by the caller.
Generics are the wrong tool. You should using polymorphism to ensure the correct implementation is called based on the type.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp
{
public abstract class Flow
{
public virtual double Value { get { return new Random().Next() ; } }//these values are just for demonstration purposes
public virtual DateTime Time
{
get
{
return DateTime.MinValue.AddYears(1);
}
}
}
public class InboundFlow : Flow
{
}
public class OutboundFlow : Flow
{
}
public abstract class Fluent
{
IList<Flow> _flowCollection;
public virtual IList<Flow> FlowCollection
{
get { return _flowCollection; }
set { _flowCollection = value; }
}
private double _initialBaseflow;
public virtual double InitialBaseflow
{
get { return _initialBaseflow; }
set { _initialBaseflow = value; }
}
public Fluent()
{
FlowCollection = new List<Flow>();
}
}
public class Affluent : Fluent
{
//public new virtual IList<InboundFlow> FlowCollection { get; set; }//Keep the property polymorphic
public Affluent()
{
FlowCollection = new List<Flow>();
}
}
public class Effluent : Fluent
{
//public new virtual IList<OutboundFlow> FlowCollection { get; set; }
public Effluent()
{
FlowCollection = new List<Flow>();
}
}
class Program
{
public static DateTime SOME_DATE { get { return DateTime.MinValue; } }
public static DateTime SOME_OTHER_DATE { get { return DateTime.Now; } }
static void Main(string[] args)
{
var inbound = new InboundFlow();
var inbound2 = new InboundFlow();
var outbound = new OutboundFlow();
var a = new Affluent();
a.FlowCollection.Add(inbound);
a.FlowCollection.Add(inbound2);
FindInitialBaseflow(a);
}
private static void FindInitialBaseflow(Fluent fluent)
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.ToList().FindAll(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
}
}