Hi guys :) I am learning C# and would like some help about inherited member/field. I have a class called Car, suppose with only one engine. However, recently I want to make a race car with 2 or more engines. The field "my_Engine" inherited from parent class seems not appropriate and I leave it to null.
Problem 1 Replace_Engine(Engine new_Engine) no longer usable in Race_Car
Problem 2 Replace_Engine(Engine target, Engine new_Engine) can only be used after an explicit cast to Race_Car.
I have quite a number of subclass of Car. How can I solve these? Do I need to rewrite all of them?
Should I rewrite them all, does replacing my_Engine to an array good enough? But not all cars have more than 1 engine... using an array seems strange. I am quite confused here.
Appreciated all your help :)
public class Car
{
//can store only one engine
protected Engine my_Engine;
//Changed to virtual to let Race_Car to override
public virtual float Get_Horse_Power()
{
return my_Engine.max_Power;
}
//Changed to virtual to let Race_Car to override
public virtual void Replace_Engine(Engine new_Engine)
{
my_Engine = new_Engine;
}
}
public class Race_Car : Car
{
protected Engine[] my_Engines;
public override float Get_Horse_Power()
{
float result=0;
for(int i = 0; i <my_Engines.Length;i++)
{
result+=my_Engines.max_Power;
}
return result;
}
//Don't know which engine need to be replaced
public override void Replace_Engine(Engine new_Engine)
{
throw new System.NotImplementedException();
}
//Created a overloading method but need an explicit cast to call
public void Replace_Engine(Engine target, Engine new_Engine)
{
for(int i = 0; i <my_Engines.Length;i++)
{
if(my_Engines[i]==target)
{
my_Engines[i]= new_Engine;
return;
}
}
}
}
The base class here assumes in the API that there is only a single engine, so yes: this will be awkward to use with types that don't adhere to that API. You could perhaps do something like:
public virtual int EngineCount => 1;
public virtual void ReplaceEngine(Engine newEngine, int engineIndex = 0)
{
if(engineIndex != 0) throw new ArgumentOutOfRangeException(nameof(engineIndex));
_engine = newEngine;
}
and:
public override int EngineCount => 2;
public void override ReplaceEngine(Engine newEngine, int engineIndex = 0)
{
switch(engineIndex)
{
case 1: _secondEngine = newEngine; break;
default: base.ReplaceEngine(newEngine, engineIndex); break;
}
}
One possible way to go about this is to make the concept of an Engine abstract and let the fact that you may have more than one physical unit serving the power be of no consequence to the original design.
You start with an abstract class that represents an engine:
public abstract class Engine
{
public Engine()
{
}
internal abstract float max_Power { get; }
}
You then implement the two flavors of engines. A simple one:
public class SimpleEngine : Engine
{
private float _maxPower;
public SimpleEngine(float maxPower)
{
_maxPower = maxPower;
}
internal override float MaxPower
{
get { return _maxPower; }
}
}
And a composite one:
public class CompositeEngine : Engine
{
private List<Engine> _engines = new List<Engine>();
public CompositeEngine(params Engine[] engines)
{
_engines.AddRange(engines);
}
internal override float MaxPower
{
get { return _engines.Sum(e => e.MaxPower); }
}
}
Usage:
var engine = new CompositeEngine(new SimpleEngine(5), new SimpleEngine(4), new SimpleEngine(3));
var car = new Car();
car.Replace_Engine(engine);
var value = car.Get_Horse_Power();
Output:
value = 12
Related
I'm trying to create unit tests for the GetOutOfJail method but I can't work out a way of getting to it as it private and apart from testing there is no need for it to be public. I can't change the signature of the LandedOnTile method as it inheriting a the abstract class Tile.
As you probably worked out, it is for a game of Monopoly I'm trying to make as a mini project.
public abstract class Tile
{
public abstract int Location { get;}
public abstract void LandedOnTile(Player player);
}
public class JailTile : Tile
{
public override int Location { get; }
Random dice = new Random();
public JailTile()
{
Location = 3;
}
public override void LandedOnTile(Player player)
{
if (player.inJail)
{
GetOutOfJail(player);
}
else
{
Console.WriteLine(player.name + " is just visiting jail");
}
}
private void GetOutOfJail(Player player)
{
int roll = dice.Next(1, 4);
int turnsInJail = player.timeInJail;
if (turnsInJail == 3)
{
player.inJail = false;
Console.WriteLine(player.name + " has spent 3 turns in jail and is now out");
player.timeInJail = 0;
}
else if (turnsInJail < 3 && roll > 2)
{
player.inJail = false;
Console.WriteLine(player.name + " has rolled a 3 and it out of jail");
player.timeInJail = 0;
}
else
{
Console.WriteLine(player.name + " has rolled a lower than a 3 and is in jail for another turn");
player.timeInJail++;
}
}
}
As mentioned by others, it shouldn't matter what the private method does from the perspective of the unit test. All you care about is that if you poke or prod the object in the right way, it ends up in the right state.
Here is how you could achieve that using interfaces and Moq.
Firstly, extract the interface that represents the properties and methods you require to perform the action. I have abstracted out your Console.WriteLine because it makes testing much easier (and even opens other opportunities for that code to be used in a non-console application). We don't actually need a "dice" per se. What we actually need is an object we can ask to Roll() and get an int. Players probably have their own business rules about them, so extracting to an IPlayer interface allows my tests of JailTile to ignore such things.
public interface ILogger
{
void LogMessage(string message);
}
public interface IDice
{
int Roll();
}
public interface IPlayer
{
string Name
{
get;
}
bool InJail
{
get;
set;
}
int TimeInJail
{
get;
set;
}
}
Secondly, here are the concrete implementations of a Dice and a ConsoleLogger. You would pass in these in your production code rather than the mocks that I use in the test cases
public class ConsoleLogger : ILogger
{
public void LogMessage(string message)
{
Console.WriteLine(message);
}
}
public class Dice : IDice
{
private readonly Random random = new Random();
public int Roll()
{
return this.random.Next(1, 6);
}
}
Thirdly, here are your Tile and JailTile classes slightly modified to use constructor injection
public abstract class Tile
{
protected readonly IDice Dice;
protected readonly ILogger Logger;
protected Tile(ILogger logger, IDice dice)
{
this.Logger = logger;
this.Dice = dice;
}
public abstract int Location
{
get;
}
public abstract void LandedOnTile(IPlayer player);
}
public class JailTile : Tile
{
public JailTile(ILogger logger, IDice dice): base (logger, dice)
{
}
public override int Location => 3;
public override void LandedOnTile(IPlayer player)
{
if (player.InJail)
{
this.GetOutOfJail(player);
}
else
{
this.Logger.LogMessage($"{player.Name} is just visiting jail");
}
}
private void GetOutOfJail(IPlayer player)
{
int roll = this.Dice.Roll();
int turnsInJail = player.TimeInJail;
if (turnsInJail == 3)
{
player.InJail = false;
this.Logger.LogMessage($"{player.Name} has spent 3 turns in jail and is now out");
player.TimeInJail = 0;
}
else if (turnsInJail < 3 && roll > 2)
{
player.InJail = false;
this.Logger.LogMessage($"{player.Name} has rolled a 3 and it out of jail");
player.TimeInJail = 0;
}
else
{
this.Logger.LogMessage($"{player.Name} has rolled a lower than a 3 and is in jail for another turn");
player.TimeInJail++;
}
}
}
Finally, here is a test case to prove that your jailTile.LandedOnTile() method causes the right changes to Player and logs the right message to console given a certain set of preconditions
[Test]
public void ShouldReleaseAfterThreeTurns()
{
// Arrange
Mock<ILogger> loggerMock = new Mock<ILogger>();
Mock<IDice> diceMock = new Mock<IDice>();
diceMock.Setup(s => s.Roll()).Returns(2);
Mock<IPlayer> playerMock = new Mock<IPlayer>();
playerMock.Setup(s => s.Name).Returns("Adam G");
playerMock.Setup(s => s.InJail).Returns(true);
playerMock.Setup(s => s.TimeInJail).Returns(3);
// Act
JailTile jailTile = new JailTile(loggerMock.Object, diceMock.Object);
jailTile.LandedOnTile(playerMock.Object);
// Assert
playerMock.VerifySet(v => v.InJail = false, Times.Once());
playerMock.VerifySet(v => v.TimeInJail = 0, Times.Once());
loggerMock.Verify(v => v.LogMessage("Adam G has spent 3 turns in jail and is now out"), Times.Once());
}
Now you probably want to think a bit more about the design, and whether it is really the tile's responsibility to be updating these properties, or whether it should call something on a jail object that can separately be tested, but this shows how you can use mocks to abstract calls to random etc out of your code to make it testable.
Is it possible to do the following specialization in C#? I can do this in C++ but do not understand how to achieve the same result in C#.
class GenericPrinter<T>
{
public void Print()
{
Console.WriteLine("Unspecialized method");
}
}
class GenericPrinter<int>
{
public void Print()
{
Console.WriteLine("Specialized with int");
}
}
Added:
The problem with suggested GenericPrinterInt solution is that I need to explicitly create it. new GenericPrinter<int> will still print Unspecialized method.
What I want is to use this GenericPrinter from another generic class without the knoledge is T equal to int or something else.
I guess the closer you could get in C# would be:
class GenericPrinter<T>
{
public virtual void Print()
{
Console.WriteLine("Unspecialized method");
}
}
class IntPrinter : GenericPrinter<int>
{
public override void Print()
{
Console.WriteLine("Specialized with int");
}
}
Otherwise, the answer is, you can't specialize in C#.
As Lyubomyr Shaydariv said in his comment:
C++ templates are not .NET generics. You can't.
From your edit I guess you will have some type checking to make.
You can do this with a dictionary for example.
class GenericPrinter<T>
{
private Dictionary<Type, Action> _actions
= new Dictionary<Type, Action>()
{
{ typeof(int), PrintInt }
};
public virtual void Print()
{
foreach (var pair in _actions)
if (pair.First == typeof(T))
{
pair.Second();
return ;
}
Console.WriteLine("Unspecialized method");
}
public virtual void PrintInt()
{
Console.WriteLine("Specialized with int");
}
}
Like you can see, you will have to make a method for each type, you want to handle. And you may also encounter some issues when you will try to manipulate T as int. Since, T is really generic (it hasn't any constraint), it will more likely act as an object in your code (not at runtime) you will have to cast it like that (int)(object)yourTVariable in your methods where you are sure that T is an int.
But for this part, I guess some of my peers, will have a better answer than me to give to you.
If it's just about displaying which type you are using:
public virtual void Print()
{
Console.WriteLine($"Specialized with {typeof(T).Name}");
}
But you won't have the unspecialized message anymore (and if you think about it, you can't have a GenericPrinter instantiated without specifying its type. Then it makes no sense to have a method that displays "unspecialized", you will always have a specified type)
Anyway, the answer is still the same, you can't specialize a generic in C#.
It isn't possible in C#.
You can use inheritance instead:
class GenericPrinter<T>
{
public virtual void Print()
{
Console.WriteLine("Unspecialized method");
}
}
class GenericPrinterInt : GenericPrinter<int>
{
public override void Print()
{
Console.WriteLine("Specialized with int");
}
}
According to the updated question, I can only suggest you the following approach. You could create a static factory method in which you can check the type of T and instantiate an appropriate specialized class if the type matches the criteria:
class GenericPrinter<T>
{
public static GenericPrinter<T> Create()
{
if (typeof(int).IsAssignableFrom(typeof(T)))
return (GenericPrinter<T>)(object)new GenericPrinterInt();
if (typeof(double).IsAssignableFrom(typeof(T)))
return (GenericPrinter<T>)(object)new GenericPrinterDouble();
// Other types to check ...
return new GenericPrinter<T>();
}
public virtual void Print()
{
Console.WriteLine("Unspecialized method");
}
}
class GenericPrinterInt : GenericPrinter<int>
{
public override void Print()
{
Console.WriteLine("Specialized with int");
}
}
class GenericPrinterDouble : GenericPrinter<double>
{
public override void Print()
{
Console.WriteLine("Specialized with double");
}
}
Some other generic class:
class SomeGenericClass<T>
{
public readonly GenericPrinter<T> Printer = GenericPrinter<T>.Create();
}
Usage sample:
var intClass = new SomeGenericClass<int>();
intClass.Printer.Print();
// Output: Specialized with int
var doubleClass = new SomeGenericClass<double>();
doubleClass.Printer.Print();
// Output: Specialized with double
var stringClass = new SomeGenericClass<string>();
stringClass.Printer.Print();
// Output: Unspecialized method
You can do it but you need to move your code into lambda expressions
or some flavor of lambdas.
It's not pretty but is fast ( no lookups ) and has the specialization.
You can tailor this to your needs
Cummon Microsoft we shouldn't have to do this.
How many improvements to .Net and no specialization.
public class GenericPrinter<T>
{
public static GenericPrint()
{
T thing = default(T)
switch(thing)
{
case int ival:
_Print = ()=>
{
Console.WriteLine("Specialized Int print Code");
};
break;
default:
_Print = ()=>
{
Console.WriteLine("Some generic print code");
};
break;
}
}
// will be unique for every type of T
public static Action _Print=null;
public void Print()
{
_Print();
}
}
Use would be the same
var printer = new GenericPrinter<int>();
printer.Print();
My brain is gonna to explode. :) So I would like to get help from you.
Please, think about my question like about just programmer puzzle. (Actually. perhaps it is very easy question for you, but not for me.)
It is needed to create array of objects. For example List where T is class. (I will describe Class T below). Also it is needed create “container” that will contain this array and some methods for work with this array. For example Add(), Remove(int IndexToRemove).
Class T must have field "Container", this way each elements of our array would be able to know where is it contained and has access its container's fields and methods. Notice, that in this case Class T should have type parameter. Indeed, it is not known beforehand which container's type is used.
Let us denote this class container as A and class element (class T) as AUnit.
Code:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a.Units[0].SomeField +=100;
Console.ReadKey();
}
}
class A
{
public List<AUnit> Units;
public A()//ctor
{
Units = new List<AUnit>();
}
public void Add()
{
this.Units.Add(new AUnit(this));
}
}
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
}
Public fields should be protected or private of course, but let think about this later.
You can ask “why we create public A Container field in AUnit”? We create field public string Name{get;private set;} (actually property but nevermind). And also we would like to be able to change value of this field for example method [Class AUnit] public bool Rename(string newName)();. The main idea of this method is changing Name field only that case if no one element in array (public List Units; ) has the same name like newName. But to achieve this, Rename method has to have access to all names that is currently used. And that is why we need Container field.
Code of extended version AUnit
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.Units)
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
Ok. If you still read it let's continue. Now we need to create Class B and class BUnit which will be very similar like Class A and Class Aunit. And finally the main question of this puzzle is HOW WE CAN DO IT? Of course, I can CopyPaste and bit modify A and AUnit and create this code.
class B
{
public List<BUnit> Units; //Only Type Changing
public B()//ctor Name changing...
{
Units = new List<BUnit>();//Only Type Changing
}
public void Add()
{
this.Units.Add(new BUnit(this));//Only Type Changing
}
}
class BUnit
{
public int SomeField;
public B Container;//Only Type Changing
public string Name { get; private set; }
public A a; //NEW FIELD IS ADDED (just one)
public BUnit(B container) //Ctor Name and arguments type changing
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
this.a=new A(); //New ROW (just one)
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (BUnit unt in this.Container.Units) //Only Type Changing
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
And I can to use this classes this way.
static void Main(string[] args)
{
B b = new B();
b.Add();
b.Units[0].a.Add();
b.Units[0].a.Units[0].SomeField += 100;
bool res= b.Units[0].a.Units[0].Rename("1");
res = b.Units[0].a.Units[0].Rename("1");
Console.ReadKey();
}
This construction is can be used to create “non-homogeneous trees”.
Help, I need somebody help, just no anybody…. [The Beatles]
I created B and BUnit using CopyPaste.
But how it can be done using “macro-definitions” or “Generic”, inherit or anything else in elegant style? (C# language)
I think that there is no reason to describe all my unsuccessful attempts and subquestions. Already topic is too long. : )
Thanks a lot if you still read it and understand what I would like to ask.
You need to implement a base type, lets call it UnitBase, with all common functionality. I'd structure your code the following way:
Create an interface for your container, this way you can change implementation to more performant solutions without modifying the elements you will be adding to the container.
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
}
Following the idea stated in 1, why not make the search logic belong to the container? It makes much more sense, as it will mostly depend on how the container is implemented:
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
bool Contains(string name);
}
A specific implementation of IContainer could be the following:
public class Container : IContainer
{
public Container()
{
list = new List<UnitBase>();
}
private List<UnitBase> list;
public Q Add<Q>() where Q: UnitBase, new()
{
var newItem = Activator.CreateInstance<Q>();
newItem.SetContainer(this);
list.Add(newItem);
return newItem;
}
public IEnumerable<UnitBase> Units => list.Select(i => i);
public bool Contains(string name) =>
Units.Any(unit => unit.Name == name);
}
Create a base class for your AUnit and BUnit types condensing all common functionality:
public abstract class UnitBase
{
protected UnitBase()
{
}
public IContainer Container { get; private set; }
public int SomeField;
public string Name { get; private set; }
public void SetContainer(IContainer container)
{
Container = container;
}
public bool Rename(String newName)
{
if (Container.Contains(newName))
return false;
this.Name = newName; //No need to use String.Copy
return true;
}
}
Implement your concrete types:
public class BUnit : UnitBase
{
public int SpecificBProperty { get; private set; }
public BUnit()
{
}
}
Shortcomings of this approach? Well, the container must be of type <UnitBase>, I've removed the generic type because it really wasn't doing much in this particular case as it would be invariant in the generic type.
Also, keep in mind that nothing in the type system avoids the following:
myContainer.Add<BUnit>();
myContainer.Add<AUnit>();
If having two different types in the same container is not an option then this whole set up kind of crumbles down. This issue was present in the previous solution too so its not something new, I simply forgot to point it out.
InBetween , I am very thankful to you for your advices. Actually I can't say that I understood your answer in full, but using your ideas I have done what I want.
Looks like my variant works well. However I would like to hear your (and everyone) opinions about code described below. The main goal of this structure is creating non-homogeneous trees. So could you estimate it from this side.
First of all. We need to create interfaces for both classes. We describe there all "cross-used" functions.
public interface IUnit<T>
{
string Name { get;}
void SetContainer(T t);
bool Rename(String newName);
}
public interface IContainer
{
bool IsNameBusy(String newName);
int Count { get; }
}
Next. Create Base for Unit Classes for future inheritance. We will use in this inheritors methods from Container Base so we need generic properties and IUnit interface.
class UnitBase<T> : IUnit<T> where T : IContainer
Unfortunately I don't know yet how to solve the problem with Constructor parameters. That is why I use method
SetContainer(T container).
Code:UnitBase
class UnitBase<T> : IUnit<T> where T : IContainer
{
protected T Container;
public string Name { get; private set; }
public UnitBase()
{
this.Name = "Default";
}
public void SetContainer(T container)
{
this.Container = container;
}
public bool Rename(String newName)
{
bool res = Container.IsNameBusy(newName);
if (!res) this.Name = String.Copy(newName);
return !res;
}
}
Next. Create ContainerBase
ContainerBase should:
1) has IContainer interface.
2)has information about what it will contain:
... where U : IUnit<C>, new()
3)and .... has information about what itself is. This information we need to pass as parameter to SetContainer() method.
Code ContainerBase:
class ContainerBase<U, C> : IContainer //U - Unit Class. C-Container Class
where U : IUnit<C>, new()
where C : ContainerBase<U, C>
{
protected List<U> Units;
public U this[int index] { get { return Units[index]; } }
public ContainerBase()//ctor
{
this.Units = new List<U>();
}
public void Add()
{
this.Units.Add(new U());
this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C>
}
public bool IsNameBusy(String newName)
{
bool res = false;
foreach (var unt in this.Units)
{
if (unt.Name == newName)
{
res = true;
break;
}
}
return res;
}
public int Count { get { return this.Units.Count; } }
}
Cast ((TContainer)(this)) may be is a bit strange. But using ContainerBase we always should use NewInheritorContainer. So this cast is just do nothing…looks like...
Finally. This classes can be used like in this example.
class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}}
class SheetUnit : UnitBase<SheetContainer>
{
public CellContainer Cells;
public PictureContainer Pictures;
public SheetUnit()
{
this.Cells = new CellContainer();
this.Pictures = new PictureContainer();
}
}
class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } }
class CellUnit : UnitBase<CellContainer>
{
public string ValuePr;//Private Field
private const string ValuePrDefault = "Default";
public string Value//Property for Value
{
//All below are Just For Example.
get
{
return this.ValuePr;
}
set
{
if (String.IsNullOrEmpty(value))
{
this.ValuePr = ValuePrDefault;
}
else
{
this.ValuePr = String.Copy(value);
}
}
}
public CellUnit()
{
this.ValuePr = ValuePrDefault;
}
}
class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } }
class PictureUnit : UnitBase<PictureContainer>
{
public int[,] Pixels{get;private set;}
public PictureUnit()
{
this.Pixels=new int[,]{{10,20,30},{11,12,13}};
}
public int GetSizeX()
{
return this.Pixels.GetLength(1);
}
public int GetSizeY()
{
return this.Pixels.GetLength(0);
}
public bool LoadFromFile(string path)
{
return false;
}
}
static void Main(string[] args)
{
SheetContainer Sheets = new SheetContainer();
Sheets.Add();
Sheets.Add();
Sheets.Add();
Sheets[0].Pictures.Add();
Sheets[1].Cells.Add();
Sheets[2].Pictures.Add();
Sheets[2].Cells.Add();
Sheets[2].Cells[0].Value = "FirstTest";
bool res= Sheets[0].Rename("First");//res=true
res=Sheets[2].Rename("First");//res =false
int res2 = Sheets.Count;
res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13
res2 = Sheets[2].Pictures.Count;//1
res2 = Sheets[1].Pictures.Count;//0
res2 = Sheets[0].Pictures[0].GetSizeX();//3
Console.ReadKey();
}
Looks like it works like I want. But I didn’t test it full.
Let me say Thank you again, InBetween.
Obviously using virtual and override is the normal situation, but does this telecoms'ish example count?
public class Pipe
{
// whole bunch of protected member variables such as bandwidth, latency, download limit
// etc,
public int GetCost()
{
// work out cost based on above
}
}
public class BigFatPipe : Pipe
{
public BigFatPipe()
{
// sets up the member variables one way
}
}
public class CheapestPossiblePipe: Pipe
{
public CheapestPossiblePipe()
{
// sets up the member variables another way
}
}
then you might call
PrintPrice(new BigFatPipe())
PrintPrice(new CheapestPossiblePipe())
public void PrintPrice(Pipe pipe)
{
int a = pipe.GetCost();
....
}
You'll get two different answers. This isn't the most useful example but does it count?
This post here has a useful discussion of what exactly polymorphism is.
I think most definitions do not explicitly state that an object must have virtual functions to be polymorphic - so yes, I think your example counts.
Constructor overloading is a recognized method to implement static polymorphism. While this isn't really constructor overloading, it's close. So yes, I'd call it polymorphism.
This pattern does work, but introducing a bunch of classes will confuse the user uselessly: they will wonder what the classes do differently.
A few factories methods will do the same job and will be easier to understand and maintain:
public class Pipe
{
// whole bunch of private member variables such as bandwidth, latency, download limit
// etc,
public int GetCost()
{
// work out cost based on above
}
public static Pipe MakeBigFatPipe()
{
var result = new Pipe();
// sets up the member variables one way
return result;
}
public static Pipe MakeCheapestPossiblePipe()
{
var result = new Pipe();
// sets up the member variables another way
return result;
}
}
If I were you I would use folowing approach:
public interface IGetCost
{
int GetCost();
}
public class Pipe : IGetCost
{
public int GetCost(){}
}
public class BigFatPipe : IGetCost
{
//aggregation
private readonly Pipe _pipe;
public BigFatPipe(Pipe pipe)
{
_pipe = pipe;
}
public int GetCost() { }
}
public class CheapestPossiblePipe : IGetCost
{
private readonly Pipe _pipe;
public CheapestPossiblePipe(Pipe pipe)
{
_pipe = pipe;
}
public int GetCost() { }
}
public static void PrintPrice(IGetCost obj)
{
int cost = obj.GetCost();
Console.WriteLine(cost);
}
static void Main(string[] args)
{
IGetCost p;
p = new Pipe();
PrintPrice(p);
p = new BigFatPipe();
PrintPrice(p);
p = new CheapestPossiblePipe();
PrintPrice(p);
}
I also need to say that there're two different things - polymorphism and overloading
polymorphism
public class foo
{
public virtual void foo1{/*....*/}
}
public class fooA : foo
{
public override void foo1{/*....*/}
}
public class fooB : foo
{
public new void foo1{/*....*/}
}
public class fooC : foo
{
//new is the default modifier
public void foo1{/*....*/}
}
overloading
public class foo{
public int foo1{/*....*/}
public int foo1(int a){/*....*/}
public int foo1(string a){/*....*/}
public int foo1(int a, string b){/*....*/}
}
Methods specific for customers:
I try to refactore a code, where are a lot of logic for specifi customer:
public void SendDocumentsToCustomer(List<Case> cases)
{
foreach(var case in cases)
{
if(case.CustomerId==123)
{
if(case.Type==1 || case.Type==2)
{
SendDocumentsToCustomer123(case)
}
else if(case.CustomerId==456)
{
if(case.Type==1 || case.Type==3)
{
SendDocumentsToCustomer456(case);
}
}
else if(case.CustomerId==768)
{
if(case.Type==2)
{
SendDocumentsToCustomer456(case);
}
else
{
SendDocumentsToCustomer(case);
}
}
}
The list of specific customer will grow, and the conditions will be modified as well. I will have a generic solution, but maybe code like this with method DoItForClient123 is not a bad solution and I should leave it like that and goint this way introduce methods like CanDocumentsBeSendToClient123 and so on?
I will be very gratefull for some input
To separate logic for each specific customer I would use such code:
abstract class DocumentSender //Base class for all document sending components
{
public abstract bool CanSend(Case #case); // Check if sender can send the document
public abstract void SendDocument(Case #case); // Send the document
}
class DefaultDocumentSender : DocumentSender
{
public override bool CanSend(Case #case)
{
return true; //Can process all requests
}
public override void SendDocument(Case #case)
{
// Do something
}
}
class Customer123DocumentSender : DocumentSender
{
public override bool CanSend(Case #case)
{
return #case.CustomerId == 123; //Specific case
}
public override void SendDocument(Case #case)
{
if(#case.Type==1 || #case.Type==2)
{
// Do something different
}
}
}
//Separate class for getting the correct sender
class CaseSenderFactory
{
readonly List<DocumentSender> _senders = new List<DocumentSender>();
public DocumentSenderFactory()
{
//Initialize the list of senders from the most specific.
_senders.Add(new Customer123DocumentSender());
// Add more specific cases here
_senders.Add(new DefaultDocumentSender()); //Last item should be the default sender
}
public DocumentSender GetDocumentSender(Case #case)
{
//At least one sender needs to satisfy the condition
return _senders.First(x => x.CanSend(#case));
}
}
You then can use the senders like this:
var factory = new DocumentSenderFactory();
foreach(var #case in cases)
{
var sender = factory.GetDocumentSender(#case);
sender.SendDocument(#case);
}
I think it would be a good ideea to make something like this:
The ideea is if the code is really specific to some of the Customers then you could make a class for them. If the code for specific customers somehow related but combined in a diferent way then you should take a loot at DecoratorPattern(mabye it helps)
class Customer
{
public abstract SendDocumentsTo(Customer c);
}
class SpecificCustomerA
{
public overwrite SendDocumentsTo(Customer c)
{
if (c is SpecificCustomerB)
{
//Logic here
}
}
}
class SpecificCustomerB { ... }