I'm currently working on a codebase and struggling to find an optimal and clean solution. I've removed the context of the problem to help simplify it to its root components. The Scale property is a simplification for a more complex state of the class in the actual codebase. I have an idea (which I'll reference at the bottom) for how I could solve this issue - however the solution feels messy and just avoids the area I want to better understand.
Class Hierarchy
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
}
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
Somewhere else in code:
public class Main
{
Child aChild = new Child();
int aChildTrueScale = aChild.GetTrueScale();
}
Expected Result: 4 (4×1) (Refer to Edit 1)
Actual Result: 16 (4×4)
Desired Result: 64 (4×8×2×1)
I want a child to find its relative scale by taking in all factors of scale from its parents, so that would like:
child relative scale = child scale × parent scale × … × base class scale
How can I (if possible) define the GetTrueScale method once in the parent class to get the desired result - which all children inherit - to avoid continuously overriding the method with duplicate implementations (the exception being the GreatGrandparent).
"Messy" Solution
Define a separate property/field in each class, and continuously override the aChildTrueScale() method with a return of ClassScale * base.GetTrueScale() where the ClassScale is a different property on each Class.
Edit 1
The expected result was my initial expectation based on my understanding at the time - thinking that within a base call the Scale reference would respect the change in scope change value to match that of the base class. With some further testing it appears that regardless of what scope when a base method is called, the referenced Scale value is always from the initial objects scope (hence 4*4).
Is it possible to refer to properties based on their scope? So in a base.GetTrueScale() call, any references within that function call will be on the base scope. Or am I completely missing something/trying to over simplify children?
Footnote
I've got a a bit of experience with procedural programming around data science, however I'm fairly inexperienced with object-oriented programming so forgive me if I'm ignorant to some core concepts. I’m happy to help clarify anything, thanks for taking the time to look over my first question! ^-^
(If anyone can think of a better title please let me know and I'll fix it up - was struggling to define the issue simply)
From the discussion in the comments, it turns out that you don't need your Scale property to be settable (its value is fixed on construction), and you don't even need it to be virtual. You can just have a single property on the GreatGrandfather class, like so:
public class GreatGrandparent
{
public int Scale { get; }
public GreatGrandparent(int scale)
{
Scale = scale;
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(int scale) : base(scale * 2) { }
}
public class Parent : Grandparent
{
public Parent(int scale) : base(scale * 8) { }
}
public class Child : Parent
{
public Child(int scale) : base(scale * 4) { }
}
In your actual code, you're dealing with a HashSet. You could write something like this, where each child adds new items to the GreatGrandparent's HashSet:
public class GreatGrandparent
{
protected HashSet<string> hashSet = new();
public GreatGrandparent()
{
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent()
{
hashSet.Add("itemTwo");
}
}
Or pass down the items to add in the constructor chain (more expensive, but maybe neater?):
public class GreatGrandparent
{
protected HashSet<string> hashSet;
public GreatGrandparent(IEnumerable<string> items)
{
hashSet = new(items);
hashSet.Add("itemOne");
}
}
public class Grandparent : GreatGrandparent
{
public Grandparent(IEnumerable<string> items) : base(items.Concat(new[] { "itemTwo" })) { }
}
I'd suggest using the type system to model this hierarchy is a mistake. You're wanting Child, Parent, GrandParent to be separate, independent things. That doesn't suggest the is-a relationship that you typically expect in the type system.
So instead have:
public class Thingy {
public int Scale {get;set;}
public Thingy Parent {get;set;}
public int GetTrueScale()
{
var current = this;
var accumulator = current.Scale;
current = current.Parent;
while(current!=null)
{
accumulator = accumulator * current.Scale;
current = current.Parent;
}
return accumulator;
}
}
And then create each of your objects:
var greatGrandParent = new Thingy {Scale = 1};
var grandParent = new Thingy {Scale = 2, Parent = greatGrandParent};
var parent = new Thingy {Scale = 8, Parent = grandParent};
var child = new Thingy { Scale = 4, Parent = parent};
And you can now call child.GetTrueScale() and all levels of the hierarchy are taken into consideration.
Adding Children sets to Thingy and other more interesting behaviours is left as a exercise.
Thingy itself could also be an IThingy interface, if the different levels truly do require separate types.
The type hierarchy will be called in the order from most base type -> most derived.
As you do not have overriden methods in Parent then your Scale is not multiplied. That it is a reason why you got 16. It is better to debug and see order of execution of your code.
You can add override GetTrueScale() method of class Parent to have desired value 64. The whole code will look like this:
public class GreatGrandparent
{
public virtual int Scale { get; set; } = 1;
public virtual int GetTrueScale()
{
Console.WriteLine("GreatGrandparent: " + Scale);
return Scale;
}
}
public class Grandparent : GreatGrandparent
{
public override int Scale { get; set; } = 2;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
public class Parent : Grandparent
{
public override int Scale { get; set; } = 8;
public override int GetTrueScale()
{
Console.WriteLine("Grandparent: " + Scale);
return Scale * base.GetTrueScale();
}
}
and Child class:
public class Child : Parent
{
public override int Scale { get; set; } = 4;
}
Related
I thought I understood generic constraints until I ran across this.
public class DBase<T> : DbContext, IDisposable where T : DBase<T>
How can T be DBase<T>?
And if it can, what does it mean?
This code compiles and runs fine. I'm not fixing a problem. I just don't understand it.
It is used here
public class ChildDb : DBase<ChildDb>
Which, again, doesn't compute for me. It passes itself as a type parameter?
How can T be DBase<T>?
There is no limitation that prevents a Generic Parameter from deriving from itself. While it's not directly understandable with the example you've given. What about a Vertex / Vertice?
Excerpt from Wikipedia:
In geometry, a vertex (plural: vertices or vertexes) is a point where two or more curves, lines, or edges meet. As a consequence of this definition, the point where two lines meet to form an angle and the corners of polygons and polyhedra are vertices.1
How does one describe a Vertex (a point)?
// very simplified example
public class Vertex
{
public int X { get; set; }
public int Y { get; set; }
}
Now how do we add a collection of relationed Verticies to this class but only allow things that derive from this class?
public class Vertex<TVertex> : Vertex
where TVertex : Vertex<TVertex>
{
public IEnumerable<TVertex> Vertices { get; set; }
}
It a generic version of say:
public Vertex2
{
public IENumerable<Vertex2> Vertices { get; set; }
}
However when I derive from Vertex2, my Vertices will always have to be IEnumerable<Vertex2>, and the correct way to allow Vertices to be a derived class is to use this type of self-reference generic.
I'm sorry Erik, I lost the point in the details. What have I gained by the recursion?
Using Vertex2, our derived types lose access to other derived properties:
public class MyVertex2: Vertex2
{
public int Id { get; set; }
}
so
var a = new MyVertex2 {Id = 1 };
var b = new MyVertex2 { Id = 2 };
a.Vertices = new List<Vertex2> { b };
b.Vertices = new List<Vertex2> { a };
// can't access Id because it's a Vertex2 not a MyVertex2
var bId = a.Vertices.First().Id;
Sure you could cast it, but then you're casting it everywhere (that's not DRY)... and what if it's not a MyVertex (MullReferencesException or InvalidCastException).
public class MyVertex: Vertex<MyVertex>
{
public int Id { get; set; }
}
var a = new MyVertex {Id = 1 };
var b = new MyVertex { Id = 2 };
a.Vertices = new List<MyVertex > { b };
b.Vertices = new List<MyVertex > { a };
var bId = a.Vertices.First().Id;
// or even
var aId = a.Vertices.First().Vertices.First();
each time we navigate to a vertices we get the correct derived type, not the base class.
John Wu posted a great blog in the comments, the TLDR of which is:
This code pattern allows you to declare a superclass that must be extended (possibly not by you, if you're writing a library that other people will use) in order to be used, but can have a bunch of methods/signatures (written by you) that return T when you write them but in practice will return objects of the child type (not written by you/you cannot know) so they can be used in a chained fashion (like the way most StringBuilder methods return the StringBuilder itself so the user can call .Append().AppendLine() ) without needing to be cast (in the code not written by you) from the parent type (written by you) to the child type (not written by you)
There's a caveat: it's not particularly useful because only the deepest child in an inheritance tree can be instantiated. Avoid using it
As a useful example, it allows you to have some methods or properties in the base class which return derived type.
For example, in the fluent builders which have chainable methods, let's say we have a base builder which set some common properties. What should be the output type of these methods?
See the following example:
public abstract class Control
{
public string Id { get; set; }
}
public abstract class ControlBuilder<TBuilder, TControl>
where TBuilder : ControlBuilder<TBuilder, TControl>, new()
where TControl : Control, new()
{
protected TControl control;
protected ControlBuilder()
{
control = new TControl();
}
public static TBuilder With()
{
return new TBuilder();
}
public TControl Build()
{
control;
}
public TBuilder Id(string id)
{
control.Id = id;
return (TBuilder)this;
}
}
Without having ControlBuilder<TBuilder, TControl> as a constraint for TBuilder, how you can return a TBuilder from Id method?
If you say ask why not return ControlBuilder<TBuilder, TControl>, because if you return it, after calling .Id("something") in method chains, it will not show derived class methods and it just will show methods of ControlBuilder<TBuilder, TControl>.
Let's say we create a TextBoxBuilder for building a TextBox:
public class TextBox : Control
{
public string Text { get; set; }
}
public class TextBoxBuilder : ControlBuilder<TextBoxBuilder, TextBox>
{
public TextBoxBuilder Text(string text)
{
control.Text = text;
return this;
}
}
Now we can use it as expected:
var txt = TextBoxBuilder.With().Id("textBox1").Text("Hello!").Build();
Below is an example of what I am trying to do.
public class Map{
int id;
int type;
List<Points>;
}
public class Points{
int xpos;
int ypos;
int id;
//Building bg; or Parking pg;
}
public Building{}
public Parking{}
Now according to the type attribute in Map class I need to add either Building or Parking class object into Points Class.
Eg: If type == 1 then add Building to Points else if type == 2 add Parking to Points.
Could anyone please help me with this ?
A way to do it, is to make both Building and Parking inherit from Point (I'd recommend a better name by the way, maybe Location).
public class Location
{
public int Id { get; }
public int X { get; }
public int Y { get; }
}
public class Building : Location
{
public int Stories { get; }
}
public class Parking: Location
{
public int Capacity { get; }
}
And now, your List<Location> inside Map can handle both buildings and parkings:
locations.Add(someBuilding);
locations.Add(someParking);
Another option is to use interfaces: interface ILocation that will be implemented by Building and Parking and a List<ILocation> in Map.
When to use one or another depends on what the commonality between the different types really is:
Inheritance: A derived type is a base class, A dog is an animal.
Interfaces: A type implementing an interface behaves like the interface. An int behaves like an IEquatable<int>, a string too. Is there anything in common between string and int besides this behavior?
I am trying to learn better programming practices using SOLID principles. Here I am working on a sample application of Shapes. I just want to know, am I breaking the principle anywhere.Below are classes and its code.
1. Base Class - Shape
public abstract class Shape
{
public abstract double Area();
public virtual double Volume()
{
throw new NotImplementedException("You cannot determine volume from here...Method not implemented.");
}
}
2. Classes for Shapes like Rectangle, Triangle etc implementing base class Shape.
public class Circle : Shape
{
public int Radius { get; set; }
public override double Area() { return 3.14 * Radius * Radius; }
}
public class Triangle : Shape
{
public int Height { get; set; }
public int Base { get; set; }
public override double Area()
{
return 0.5 * Base * Height;
}
}
public class Rectangle : Shape
{
public int Length { get; set; }
public int Breadth { get; set; }
public override double Area()
{
return Length * Breadth;
}
}
public class Square : Shape
{
public Square() { }
public int Side { get; set; }
public override double Area()
{
return Side * Side;
}
}
3. A factory class that returns Shape.
internal class ShapeFactory<K, T> where T : class, K, new()
{
static K k;
private ShapeFactory() { }
public static K Create()
{
k = new T();
return k;
}
}
Till here everything seems fine and looks good, but problem occurs when I implemented it. I am little confused here. Lets see the front end code first:
internal class Program
{
private static void Main(string[] args)
{
try
{
var c = ShapeFactory<Shape, Circle>.Create();
// this part is not clear to me. See the questions below
if(c is Circle)
{
var circle = c as Circle;
circle.Radius = 5;
Console.WriteLine(string.Format("{0}", circle.Area()));
}
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
Console.Read();
}
}
QUESTIONS
Different shapes has got different properties like circle has Radius, triangle has base and height and so on , so i decided to keep my properties in child class. I knew, I can have that as virtual member in my base class. So Is there any way other than coded above.
If not, then what is the use of abstract class, if still I am typecasting my Shape object to circle object? I can simple use Circle c = new Circle(). I don't want unwanted checks like (if c is circle) and all.
What If , I am asked to implement a new method to get Circumference of a circle. Do I need to create a new Abstract class or put it in Circle class. But if I put it Circle, I think it will break very first principle of SOLID i.e. SRP
.
Kindly note, I don't my abstract class as a fat class having unnecessary or repeated properties.
Thanks in advance
What I usually do in this case is to pass constructor parameters in concrete classes. So i'd change your concrete shapes to something like:
public class Circle : Shape
{
public int Radius { get; set; }
public Circle(int radius) {
this.Radius = radius;
}
public override double Area() { return 3.14 * this.Radius * this.Radius; }
}
public class Rectangle : Shape
{
public int Length { get; set; }
public int Breadth { get; set; }
public Rectangle(int lenght, int breadth) {
this.Length = lenght;
this.Breadth = breadth;
}
public override double Area()
{
return Length * Breadth;
}
}
and so on
Now, I would use a factory method, so your fabric will now be like:
public abstract class ShapeFactory
{
abstract Create();
}
public class CircleFactory : ShapeFactory
{
private int radius;
public CircleFactory(int radius){
this.radius = radius;
}
protected override Shape Create()
{
return new Circle(this.radius);
}
}
public class RectangleFactory : ShapeFactory
{
private int length;
private int breadth;
public RectangleFactory(int length, int breadth){
this.lenght = length;
this.breadth = breadth;
}
protected override Shape Create()
{
return new Rectangle(this.length, this.breadth);
}
}
Notice that, now a factory know how to build a shape with constructor passed in its own constructor.
So, each time you want a diferent shape you will instantiate a new factory.
ShapeFactory factory = new CircleFactory(5);
Shape shape = factory.Create();
Console.WriteLine(shape.Area()));
I think this answer your 1st and 2nd question.
So, 3:
What you can do to dont modify your class is use the strategy pattern in order to pass at runtime how to implement this method:
public interface IPerimeter
{
int calculatePerimeter();
}
public class Circunference : IPerimeter
{
public int calculatePerimeter(Circle circle) {
return 2*pi*circle.radius;
}
}
public class Circle : Shape
{
public int Radius { get; set; }
private IPerimeter perimeter;
public Circle(int radius, IPerimeter perimeter) {
this.Radius = radius;
this.perimeter = perimeter;
}
public Circunference() {
perimeter.calculatePerimeter(this);
}
public override double Area() { return 3.14 * this.Radius * this.Radius; }
}
Hope this helps with your training.
Different child classes will have different properties, that's expected and ok. Normally not all derived classes have the exact same properties as their base class. There's no reason to force Shape to have a Radius. What advantage would you have? That's just opening the door for trouble. What's your ultimate goal with this? Having something like myShape.Dimension = value and not care if it's a radius, a side, etc.? Anything can be done, depending on your needs.
With your abstract class you can, for example, loop through a list of Shape and call Area() or Volume(), knowing that you will get your result (despite your still not implemented Volume). Also your base class could have some common code, which in this case you are not using. You could have for example a Unit property which could be cm, inches, meters, etc. and then have a method like this (silly example):
public string GetAreaString()
{
return string.Format("{0} {1}", this.Area().ToString(), this.Unit);
}
Just implement it in Circle, of course. Why would it break Circle's single responsibility? Your class is dealing with the calculation of its
related values, just like a string tells you if it's null or its length.
For me your example seems really over engineered. I think you should always implement the simplest thing that works nothing more nothing less. I know that this is an example code, because you want to learn the SOLID principles, but I think is important to be aware of how horribly wrong can go these principles in the wrong context. In your specific code: do you need to group all your shapes using the Shape class? I mean, do you ever plan to iterate through a list of Shapes and calculate the area and volume for them? If not, the inheritance has absolutely no point. In fact I would say that inheritance is overused these days, and when it is overused you end up with ugly inheritance dependency graphs. Regarding the factory class: Is construction of any of your "shape" objects particularly difficult, time consuming, tricky. Does your factory class provide some value or it is completely useless? In case it has no real reason to exist, I wouldn't use it, the new operator is far more clear.
I hope you don't mind my reply but I just wanted you to be aware of the fact that some SOLID principles applies in very specific scenarios. Forcing them in the wrong places may cause ugly and over complicated code. In some real world situations, if the questions above are answered with yes, your pattern seems OK. Otherwise the exact same pattern can over-complicate things without any real benefits. I guess my point is: just be aware, not every SOLID principle is good in any situation:).
This is extremely common problem. While learning SOLID is nice, it requires understanding of basic design principles like abstraction and indirection. The reason why you are confused is because there is no abstraction in your code.
Imagine you have code that wants to know shape's area, but it doesn't care what shape it is nor how to calculate that shape's area. Something like :
public void PrintArea(Shape shape)
{
Console.WriteLine(shape.Area());
}
This is THE CRITICAL PART of OOP design. Your example has absolutely nothing of this. Your example is just contrived piece of code that has no logic to it, let alone being SOLID.
I have create a generic Node class which will be used to build a tree object. I want to inherit the attributes of this class in another. The job class represents a SQL job which is included in a chain (tree) of jobs. The following code is giving me an error and I am not sure why.
public class Node<T>
{
public int id { get; set; }
public Node<T> parent { get; set; }
public List<Node<T>> Children = new List<Node<T>>();
public bool isRoot
{
get { return parent == null; }
}
public static Node<T> createTree(List<Node<T>> nodes)
{
if (nodes.Count() == 0)
return new Node<T>();
//Build parent / Child relationships
}
}
public class Job : Node<Job>
{
public string name {get; set;}
public Job(String name)
{
this.name = name;
}
}
List<Job> joblist = JobDict.Select(j => new Job(j.Key)).ToList();
Node<Job>.createTree(joblist);
I am Unable to call createTree with the List of Jobs. I realize changing it from List < Job > to List < Node< Job > > works but why am I unable to do the former? I figured because I am inheriting the node class, a List of Jobs would in fact be equivalent to a List of Node. I am sorry if this is a very basic question but I just began with generics and inheritance and am having a hard time grasping it entirely.
The problem is that List<Node<Job>> and List<Job> are not co-variant.
If you're using .NET 4 you can do this.
Node<Job>.createTree((IEnumerable<Node<Job>>)joblist);
or, you can modify the creeatetree method definition as follows.
public static Node<T> createTree(IList nodes)
{
if (nodes.Count == 0)
return new Node<T>();
//Build parent / Child relationships
}
I realize changing it from List<Job> to List<Node<Job>> works
but why am I unable to do the former?
Because List<Job> does not inherit List<Node<Job>> even if Job inherits Node<Job>. In other words, A inherits B does not mean List<A> inherits List<B>.
You may need to cast each Job object to Node<Job> first:
var jobNodeList = joblist.Select(j => (Node<Job>)j).ToList();
Node<Job>.createTree(jobNodeList);
This isn't a direct answer to the question, but it might give you some ideas on how to make your code simpler.
It's often a good idea to keep code simple. The simplest tree structure you can make is this:
public class Tree<T> : List<Tree<T>> { }
In your case, you would extend it slightly as:
public class Node<T> : List<Node<T>>
{
public int id { get; set; }
public Node<T> parent { get; set; }
public bool isRoot
{
get { return parent == null; }
}
}
Effectively you get all of the Children properties for free.
I have a series of objects, lets call them buildings, that each share certain properties that are static for that building, but different for each building, such as price. I assumed that the best way to implement this was to create an abstract superclass with the shared price attribute and set the values in each subclass, but I cannot figure out how to get this to work. Here is an example of something I have tried:
using System;
public abstract class Buildings
{
internal static int price;
internal static int turnsToMake;
}
using System;
public class Walls : Buildings
{
public Walls()
{
price = 200;
turnsToMake = 5;
}
}
This works fine for construction, but if I want to check the price before creating it (to check if the player has enough money) then it just returns a null value. I'm sure that it is is a super simple fix, but I can't figure it out. Any help?
There is a "patchy" yet simple solution that's worth to consider. If you define your base class as a Generic class, and in deriving classes set T as the class itself, It will work.
This happens because .NET statically defines a new type for each new definition.
For example:
class Base<T>
{
public static int Counter { get; set; }
public Base()
{
}
}
class DerivedA : Base<DerivedA>
{
public DerivedA()
{
}
}
class DerivedB : Base<DerivedB>
{
public DerivedB()
{
}
}
class Program
{
static void Main(string[] args)
{
DerivedA.Counter = 4;
DerivedB.Counter = 7;
Console.WriteLine(DerivedA.Counter.ToString()); // Prints 4
Console.WriteLine(DerivedB.Counter.ToString()); // Prints 7
Console.ReadLine();
}
}
Don't use static. Static says that all instances of Building have the same value. A derived class will not inherit its own copy of the statics; but would always modify the base class statics. In your design there would only be one value for price and turnsToMake.
This should work for you:
public abstract class Buildings
{
internal int price;
internal int turnsToMake;
}
However, most people don't like using fields these days and prefer properties.
public abstract class Buildings
{
internal int Price { get; set; }
internal int TurnsToMake { get; set; }
}
I want to check the price before creating it […]
I suppose that's how you got to static fields; however, static and virtual behaviour cannot be combined. That is, you would have to re-declare your static fields for each subclass. Otherwise, all your subclasses share the exact same fields and overwrite each others' values.
Another solution would be to use the Lazy<T, TMetadata> type from the .NET (4 or higher) framework class library:
public class Cost
{
public int Price { get; set; }
public int TurnsToMake { get; set; }
}
var lazyBuildings = new Lazy<Buildings, Cost>(
valueFactory: () => new Walls(),
metadata: new Cost { Price = 200, TurnsToMake = 5 });
if (lazyBuildings.Metadata.Price < …)
{
var buildings = lazyBuildings.Value;
}
That is, the metadata (.Metadata) now resides outside of the actual types (Buildings, Walls) and can be used to decide whether you actually want to build an instance ( .Value) of it.
(Thanks to polymorphism, you can have a whole collection of such "lazy factories" and find a building type to instantiate based on the metadata of each factory.)
Building on Uri Abramson's answer above:
If you need to access the static property from within the Base class, use reflection to get the value from T. Also, you can enforce that Base must be inherited using T of the derived type.
e.g.
class Base<T> where T : Base <T> {
static int GetPropertyValueFromDerivedClass<PropertyType>(BindingFlags Flags = BindingFlags.Public | BindingFlags.Static, [CallerMemberName] string PropertyName = "")
{
return typeof(T).GetProperty(PropertyName, Flags)?.GetValue(null);
}
static int Counter{ get => GetPropertyValueFromDerivedClass(); }
}
static int DoubleCounter{ return Counter*2; } //returns 8 for DerivedA and 14 for DerivedB
}
If you have a better way to do this, please post.
Not as easy for the inheritor, but workable...
public abstract class BaseType
{
public abstract contentType Data { get; set; }
}
public class InheritedType : BaseType
{
protected static contentType _inheritedTypeContent;
public override contentType Data { get => _inheritedTypeContent; set => _inheritedTypeContent = value; }
}