Do split one class hierarchy into several or not? - c#

For now, we have a .NET application that leans on the standard .NET XML serialization/deserialization mechanism. The example is simplified but the meaning is the same.
public abstract class Shape
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("level")]
public int Level { get; set; }
public abstract void Draw();
public abstract void Clear();
public abstract void Scale(double scale);
}
[XmlType("Circle")]
public class Circle : Shape
{
public double Radius { get; set; }
public override void Draw() {}
public override void Clear() {}
public override void Scale(double scale) {}
}
[XmlType("Rectangle")]
public class Rectangle: Shape
{
public double Height { get; set; }
public double Width { get; set; }
public override void Draw() {}
public override void Clear() {}
public override void Scale(double scale) {}
}
public class Picture
{
public double Scale { get; set; }
[XmlArrayAttribute("Shapes")]
public Collection<Shape> Shapes { get; set; }
public void Setup()
{
foreach (Shape shape in Shapes)
{
shape.Draw();
}
foreach (Shape shape in Shapes)
{
shape.Scale(Scale);
}
}
public void Cleanup()
{
foreach (Shape shape in Shapes)
{
shape.Clear();
}
}
public static Picture FromXml(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(Picture));
return serializer.Deserialize(xmlReader) as Picture;
}
}
And for example the input XML file will look like:
<Picture>
<Scale>0.9</Scale>
<Shapes>
<Circle id="1">
<Radius>1.5</Radius>
</Circle>
<Circle id="2">
<Radius>3</Radius>
</Circle>
<Rectangle id="3">
<Height>300</Height>
<Width>300</Width>
</Rectangle>
</Shapes>
</Picture>
But model classes contain a logic (Draw(), Clear() and Scale() methods) and it seems that it breaks a single responsibility principle. And therefore we don't know does it make sense to split that logic into several classes or not?
If yes, then how? Because once we read the XML file, all objects are accessible only as Shape objects and therefore we will have to explicitly cast the object either before passing if to the handler class or inside that method, for example:
public abstract class Drawer
{
public abstract void Draw(Shape shape);
}
public class CircleDrawer : Drawer
{
public override void Draw(Shape shape)
{
Circle circle = shape as Circle;
if (circle == null)
{
throw new ArgumentException("Passed object is not of type Circle");
}
}
}
If that issue is known, please just redirect me to that resource.
Thank you in advance.

You should separate the model and business logic,
So the object that will be deserialized will contain only the properties.
Then, in the factory or method that creates the business logic, insert it as a member of the circle (for example) business logic.

Trying to separate model and business logic I get the following code. Is it acceptable? As for me, it looks not flexible: we should not forget to extend the if statement if we add a new shape drawer.
public abstract class Drawer
{
public abstract void Draw();
}
public class CircleDrawer : Drawer
{
private readonly Circle _circle;
public CircleDrawer(Circle circle)
{
_circle = circle;
}
public override void Draw() { }
}
public class RectangleDrawer : Drawer
{
private readonly Rectangle _rectangle;
public RectangleDrawer(Rectangle rectangle)
{
_rectangle = rectangle;
}
public override void Draw() { }
}
public class Picture
{
public double Scale { get; set; }
[XmlArrayAttribute("Shapes")]
public Collection<Shape> Shapes { get; set; }
public void Setup()
{
List<Drawer> drawers = new List<Drawer>();
foreach (Shape shape in Shapes)
{
if (shape is Circle)
{
drawers.Add(new CircleDrawer(shape as Circle));
}
else if (shape is Rectangle)
{
drawers.Add(new RectangleDrawer(shape as Rectangle));
}
else
{
}
}
foreach (Drawer drawer in drawers)
{
drawer.Draw();
}
}
public static Picture FromXml(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof(Picture));
return serializer.Deserialize(xmlReader) as Picture;
}
}

The question in this case would be, is it needed that the caller knows the concrete type or is everything mapped through the common base class?
In your example all implementations share a common abstract base class. If all users can happily work with this common behaviour you just push around all instances casted to the base type. Nevertheless as you already mentioned this tends to put more and more business logic into each concrete class. Depending on how much (or better less) this is, it is absolutely okay to let it there (this seems to be your current solution).
If this is getting more or quite complex it would be a good way to move this into its own class and/or methods. In that case you need some kind of dispatcher, that decides which takes care for the given object. I start to use a dictionary for these cases that works as a dispatcher and has the given type as key and an Action or Func as value like this:
var dispatcher = new Dictionary<Type, Action<Shape>>
{
{ typeof(Rectangle), DoSomethingWithRectangle },
{ typeof(Circle), DoSomethingWithCircle }
}
private void DoSomethingWithRectangle(Shape shape)
{
var rectangle = (Rectangle)shape;
Console.WriteLine($"Rectangle Height: {rectangle.Height} Width: {rectangle.Width}");
}
private void DoSomethingWithCircle(Shape shape)
{
var circle = (Circle)shape;
Console.WriteLine($"Circle Radius: {circle.Radius}");
}
This is just a trivial example. The dictionary should be live within a dispatcher where you have some methods to register different types and instead of methods you could also register types or instances of classes that could handle each specific instance type. If this kind of dispatching is still not matching all your cases you could maybe take a look at MediatR which formalized these things much more with a bunch of type-safe interfaces which you use to define your input types, the desired return value and an handler that is capable of doing this job.

Related

Is it possible to inherit 1 interface or another? (.NET 4.8 Framework)

I have this 2 interfaces:
internal interface ITexture
{
string Texture { get; set; }
}
internal interface IBackTexture
{
string BackTexture { get; set; }
}
Where, for context, not all Textures have a Back Texture and not all Back Textures have a Texture.
public void TextureMerger<T>(...) where T : ITexture,IBackTexture
{
...
}
This type of inheritance makes so that T needs to have both Texture and Back Texture.
Is there a way to make it so it is at least one of them?
There is no way to describe type-safe union of two non-compatible types in C#.
You can use type safe wrappers around private method accepting common base (i.e. object).
public void TextureMerger(ITexture t) => TextureMerger((object) t);
public void TextureMerger(IBackTexture t) => TextureMerger((object) t);
// to resolve ambiguous between the previous two overloads if class implements both
public void TextureMerger<T>(T t) where T : ITexture, IBackTexture => TextureMerger((object) t);
private void TextureMerger(object t)
{
if (t is ITexture it)
{
// ...
}
if (t is IBackTexture ibt)
{
// ...
}
}
Or introduce new common base (i.e. interface ITextureBase) to make them compatible.
interface ITextureBase { /*empty marker*/ }
interface IBackTexture : ITextureBase { /* ... */}
interface ITexture : ITextureBase { /* ... */}
And use it
public void TextureMerger(ITextureBase t) {}
You could create a pointless base interface to relate the acceptable types like this,
using System;
public class Program
{
public static void Main()
{
}
internal void TextureMerger<T>(params IPointlessBase[] pointlesslyBasedTextures)
where T : IPointlessBase
{
foreach(var pb in pointlesslyBasedTextures)
{
string textureString;
switch (pb)
{
case ITexture t:
textureString = t.Texture;
break;
case IBackTexture bt:
textureString = bt.BackTexture;
break;
default:
throw new NotImplementedException(pb.GetType().Name);
}
}
}
}
internal interface IPointlessBase
{
}
internal interface ITexture : IPointlessBase
{
string Texture { get; set; }
}
internal interface IBackTexture : IPointlessBase
{
string BackTexture { get; set; }
}
However, doing that feels like an anti-pattern, if your interfaces have something in common, then both those interfaces should have that property, so the property should be on the inherited base interface.
Is there a way to make it so it is at least one of them?
No, not directly at least. Generic constraints allow you to restrict the type to allow you to access properties and methods on the generic type. If the type only needs to implement one you do not know what properties and methods are allowed to be called.
The simplest workaround would be to insert runtime checks.But I would probably consider if your model is correct. You might want to build a type that wraps the multiple possible states you want to describe:
public class Texture{
public bool HasFront => string.IsNullOrWhitespace(FrontTexture);
public bool HasBack => string.IsNullOrWhitespace(BackTexture);
string FrontTexture;
string BackTexture;
private Texture(string front, string back){
FrontTexture = front;
BackTexture = back;
}
public static Texture CreateFront(string front) => new (front, null);
public static Texture CreateBack(string back) => new (null, back);
public static Texture Create(string front, string back) => new (front, back);
// Other methods to use the texture
}

Tricky Inheritance-Related Architecture

I'm trying to design a system in C# for different character types in a video game. I have a simple inheritance tree for my character types with a Humanoid base class that has several derived classes such as Human, Alien, etc. Each of these character types includes a body with several parts. Currently these parts are stored in an instance of a Body class and each part is described by an instance of BodyPart class.
The main complication comes when I want to have derived classes for BodyPart since each character type's body parts will have some additional specific properties. The main reason I want the BodyPart instances to be grouped in a Body class is so that I can use an indexer since I need to able to iterate over them and still be able to use dot notation to access specific parts easily.
The following pseudocode should give you an idea of what I'm trying to achieve and how I have already tried to implement it.
public class BodyPart
{
public float health;
//...
}
public class HumanBodyPart : BodyPart
{
public float exampleHumanStuff;
//.. human specific stuff
}
public class AlienBodyPart : BodyPart
{
public float exampleAlienStuff;
//.. alien specific stuff
}
public class Humanoid
{
public class Body<T> where T : BodyPart
{
public T head;
public T leftArm;
public T rightArm;
//...
public T this[int i]
{
get
{
switch (i)
{
case 0:
return head;
case 1:
return leftArm;
//...
}
}
}
}
public virtual Body<BodyPart> body { get; }
public void Example {
body.head.health = 50;
body[2].health = 55;
}
}
public class Human : Humanoid
{
public Body<HumanBodyPart> humanBody;
public override Body<BodyPart> body { get { return (Body<BodyPart>)humanBody; } }
public void Example
{
body.rightArm.exampleHumanStuff = 5;
}
}
public class Alien : Humanoid
{
public Body<AlienBodyPart> alienBody;
public override Body<BodyPart> body { get { return (Body<BodyPart>)alienBody; } }
public void Example
{
body.leftArm.exampleAlienStuff = 5;
}
}
The dead end with this approach is that the Body class is not contravariant (I think?) so casting a Body<HumanBodyPart> to Body<BodyPart> won't work. But I can't figure out another way to access the Body<HumanBodyPart> class instance in Human from the base Humanoid class so that in the base class it's treated as a Body<BodyPart>. Is there a better way of doing this?
You can make Humanoid generic of BodyPart itself:
public class Humanoid<T> where T : BodyPart
{
public class Body
{
public T head;
//...
public T this[int i]
{
get
{
switch (i)
{
case 0:
return head;
//...
}
return default;
}
}
}
public Body body { get; }
public void Example()
{
body.head.health = 50;
body [2].health = 55;
}
}
public class Human : Humanoid<HumanBodyPart>
{
public void Example()
{
body.rightArm.exampleHumanStuff = 5;
}
}
public class Alien : Humanoid<AlienBodyPart>
{
public void Example()
{
body.leftArm.exampleAlienStuff = 5;
}
}
Also this class hierarchy does not support all possible use-cases (for example collection of different Humanoid's, but you can workaround some with non-generic interfaces (for Humanoid and Body)).
As for variance it is supported only for interfaces and delegates (and arrays, but don't use it) in C#. In your case something like this will work:
public interface IBody<out TInner>
{
public TInner head { get; }
public TInner leftArm { get; }
public TInner this[int i] { get; }
}
IBody<HumanBodyPart> humanBody = ...;
IBody<BodyPart> body = humanBody;
I assume you want to assign a specific BodyPart of the Humanoid class. I would use this:
public class Humanoid <T> where T : BodyPart
{
public class Body<T>
{....
And in the end when you describing a Human you can use:
public class Human : Humanoid <HumanBodyPart>
{.......

c# is it possible to change the field type of parent's class?

If i have 2 classes
One for data, for example:
public class Cords
{
public double x;
public double y;
}
and one, that using this data:
public class Geometry
{
public Cords()
{
points = new List<Cords>();
}
public void SomeMathWithPoints()
{
MagicWithPoints(points);
}
protected List<Cords> points;
}
And i want to exted this class with some specific functions, using inheritance, but this time i need some aditional data for Cords class.
So i'm trying to do it this way:
public class ExtendedCords: Cords
{
public double x;
public double y;
public string name;
}
public class ExtendedGeometry : Geometry
{
protected SomeNewMagicWithPoints(){...}
protected List<ExtendedCords> points;
}
But i've noticed, that if i will do:
ExtendedGeometry myObject = new ExtendedGeometry();
myObject.SomeMathWithPoints();
This function will be using old (parrents) field points. So how can i make it use the new one with a type ExtendedCords? I mean, i want to be able to use both child's and parrent's functions on a new field.
Use generic types for the Geometry base class and virtual methods:
public class Geometry<TCord> where TCord : Cords
{
public void InitCords(){
points = new List<TCord>();
}
public virtual void SomeMathWithPoints(){
MagicWithPoints(points);
};
protected List<TCord> points;
}
Then in your extension,
public class ExtendedGeometry : Geometry<ExtendedCords>
{
public override SomeNewMagicWithPoints(){...}
// no need for a redefinition of points since it is inherited from the base class Geometry<ExtendedCords>
}

Is this breaking my SOLID Principle?

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.

How to abstract/remove redundent methods in the following class sample

I'm trying to remove the redundancy I have in my Enemy0, Enemy1, ... classes that inherit from my abstract Enemy class. This is all Monogame/XNA.
My abstract class looks like so:
public abstract class Enemy
{
public abstract Vector2 Position { get; }
public Enemy() {}
public abstract void Update(GameTime gameTime);
public abstract void Draw(SpriteBatch spriteBatch);
public abstract Part getLeftPart();
public abstract Part getRightPart();
public abstract Part getLeftLeftPart(); //This method only used in Boss0 classes
public abstract Part getRightRightPart(); //This method only used in Boss0 classes
}
Though my Enemy# classes vary in implementation, some methods are exactly the same/redundant and take up room (but have parameters relative to that class, eg the private variable _leftPartPos).
The problem occurs when I have 20 or so Enemy# classes and I decide to add an extra parameter to the Part object (which is used in getLeftPart()- I would have to also modify 20 of those classes that inherit the Enemy
public class Enemy0 : Enemy
{
private Texture2D _partTexture;
private Vector2 _leftpartPos;
private Vector2 _rightPartPos;
public override Vector2 Position
{
get { return _position; } // Reason for this get is to access inner variables
} // like _position.X = 10
private Vector2 _position;
public Enemy() {}
public abstract void Update(GameTime gameTime)
{
// Some varying functionality per inheriting class
}
public abstract void Draw(SpriteBatch spriteBatch)
{
// Some varying functionality per inheriting class
}
public override Part getLeftPart() // This always returns the same object (with
{ // different params)
return new Part(
_partTexture,
_leftPartPos,
SpriteEffects.FlipHorizontally);
}
public abstract Part getRightPart()
{
return new Part(
_partTexture,
_rightPartPos,
SpriteEffects.None);
}
public override Part getLeftLeftPart()
{
return null; // This only returns an instance in Boss0, Boss1, etc
}
public override Part getRightRightPart()
{
return null;
}
}
The reason I have this abstract class is so that I can initialize a List<Enemy> object and access its varied methods without having to cast each class as Enemy0, Enemy1, ...
Is there a way I can throw in the functionality of getLeftPart(), getRightPart(), ... into my abstract class? Is there a better approach for all this?
Declare them as virtual instead of abstract. That way you can provide a base implementation in the root class and override it in the child classes if they require different functionality.
public abstract class RootClass
{
public virtual void DoSomething()
{
Console.WriteLine("I'm doing something in the root class");
}
}
public class ChildClass1 : RootClass
{
public void SomethingElse()
{
DoSomething(); //Prints out "I'm doing something in the root class"
}
}
public class ChildClass2 : RootClass
{
public void SomethingElse()
{
DoSomething(); //Prints out "I'm doing something in ChildClass2
}
public override void DoSomething()
{
Console.WriteLine("I'm doing something in ChildClass2");
}
}
If you have to take variable numbers of parameters, declare the method with the params attribute, like:
public virtual void DoSomething(params object[] args)
This causes boxing, so be aware of the performance implications. If you need to do something more specific, try:
public virtual void DoSomething<T>(params T[] args)
But the only difference here is that all the parameters must be of the same type, or convertible to that type. If you only need one parameter of some type, use:
public virtual void DoSomething<T>(T arg)
or you can have two (or more types)
public virtual void DoSomething<T1, T2>(T1 arg1, T2 arg2)
etc, etc.

Categories

Resources