I am working on going through "Head First Design Patterns" and I am trying to properly convert the Java code into C#. Here is what I have. Can you tell me if this is a good implementation/conversion so far?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Duck
{
public abstract class Duck
{
public FlyBehavior flyBehavior;
public QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
public void performFly()
{
flyBehavior.fly();
}
public void performQuack()
{
quackBehavior.quack();
}
public void swim()
{
Console.WriteLine("All ducks float, even decoys!");
}
}
public interface FlyBehavior
{
void fly();
}
public class FlyWithWings : FlyBehavior
{
public void fly()
{
Console.WriteLine("I'm flying!");
}
}
public class FlyNoWay : FlyBehavior
{
public void fly()
{
Console.WriteLine("I can't fly!");
}
}
public interface QuackBehavior
{
void quack();
}
public class Quack : QuackBehavior
{
public void quack()
{
Console.WriteLine("Quack");
}
}
public class MuteQuack : QuackBehavior
{
public void quack()
{
Console.WriteLine("<< Silence >>");
}
}
public class Squeak : QuackBehavior
{
public void quack()
{
Console.WriteLine("Squeak");
}
}
public class MallardDuck : Duck
{
public MallardDuck()
{
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public override void display()
{
Console.WriteLine("I'm a real Mallard duck");
}
}
public class MiniDuckSimulator
{
public static void Main(string[] args)
{
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
Console.ReadLine();
}
}
}
2 Answers 2
public
fields are a bit of a no-no since that violates encapsulation. So I've changed the fields to private readonly
and inject those dependencies via the constructor. Speaking of constructor, I made it protected
as a public
constructor on an abstract
class doesn't make much sense. Also, make note of C# naming conventions which PascalCase's methods and that interface
s should start with "I
":
using System;
namespace Duck
{
public abstract class Duck
{
private readonly IFlyBehavior flyBehavior;
private readonly IQuackBehavior quackBehavior;
protected Duck(IFlyBehavior flyBehavior, IQuackBehavior quackBehavior)
{
this.flyBehavior = flyBehavior;
this.quackBehavior = quackBehavior;
}
public abstract void Display();
public void PerformFly()
{
this.flyBehavior.Fly();
}
public void PerformQuack()
{
this.quackBehavior.Quack();
}
public void Swim()
{
Console.WriteLine("All ducks float, even decoys!");
}
}
public interface IFlyBehavior
{
void Fly();
}
public class FlyWithWings : IFlyBehavior
{
public void Fly()
{
Console.WriteLine("I'm flying!");
}
}
public class FlyNoWay : IFlyBehavior
{
public void Fly()
{
Console.WriteLine("I can't fly!");
}
}
public interface IQuackBehavior
{
void Quack();
}
public class Quack : IQuackBehavior
{
public void Quack()
{
Console.WriteLine("Quack");
}
}
public class MuteQuack : IQuackBehavior
{
public void Quack()
{
Console.WriteLine("<< Silence >>");
}
}
public class Squeak : IQuackBehavior
{
public void Quack()
{
Console.WriteLine("Squeak");
}
}
public class MallardDuck : Duck
{
public MallardDuck() : base(new FlyWithWings(), new Quack())
{
}
public override void Display()
{
Console.WriteLine("I'm a real Mallard duck");
}
}
public class MiniDuckSimulator
{
public static void Main(string[] args)
{
Duck mallard = new MallardDuck();
mallard.PerformQuack();
mallard.PerformFly();
Console.ReadLine();
}
}
}
-
\$\begingroup\$ Thanks guys! I am going to look over both your answers, but I really appreciate that you took the time to explain in such depth. I am working hard to learn these design patterns and input like this is going to just speed up the process. \$\endgroup\$Michael Mahony– Michael Mahony2013年08月05日 16:21:38 +00:00Commented Aug 5, 2013 at 16:21
-
1\$\begingroup\$ Yes, I am aware that if this was a production application each class would be its own file, but since this is just to learn a strategy pattern in C# I have kept them all in the same file. The changes made by Jesse are perfect and make the code cleaner and safer, so thanks for that. I am studying it will get back to you guys if I have any questions. Thanks a million! \$\endgroup\$Michael Mahony– Michael Mahony2013年08月05日 16:29:58 +00:00Commented Aug 5, 2013 at 16:29
Oddly enough, in the Microsoft C# world, PascalCasing
(as opposed to camelCasing
) doesn't just apply to classes, but also to public fields, public methods, and public properties.
So maybe you might want to rename Duck.flyBehavior
to Duck.FlyBehavior
, Duck.quackBehavior
to Duck.QuackBehavior
, etc.
While you're at it, since those are public fields, you might also want to switch those to C# properties, instead, in case you ever needed the added functionality of sanitizing input.
before
public FlyBehavior FlyBahavior
after*
public FlyBehavior FlyBehavior { get; set; }
Although, I still write C# the old school way, but if things haven't changed, I think interfaces still are prefixed with a capital I
. So your FlyBehavior
interface will be named IFlyBehavior
. Same thing for QuackBehavior
; it will become IQuackBehavior
.
Another pedantic remark: although I have a hunch that you already know this, but classes should be in their own file*. I'm just saying, in case my hunch was wrong, and you didn't know.
* Disclaimer: I'm not exactly sure why separating classes into files is considered important. And I am in no way saying that it isn't important, just that if you want to know why, I'm afraid I can only give you my own intuition as to why I think it's important to separate classes into their own file.
-
\$\begingroup\$ "Disclaimer: I'm not exactly sure why separating classes into files is considered important." Pretty certain it's purely organisational - it's easier to find
public abstract class Duck
when the file is calledDuck.cs
rather than if everything is inMyProgram.cs
, or separated intoStuff1.cs
,Stuff2.cs
etc - easier to find the file and easier to find the location in the file where the class starts (the beginning). Edit: I think in Java each class has to be a separate file due to the way the compiler works (it expectsDuck.java
to containDuck
). Might be wrong on that though. \$\endgroup\$Kai– Kai2013年08月05日 08:11:52 +00:00Commented Aug 5, 2013 at 8:11 -
\$\begingroup\$ Although I personally employ an amount of flexibility in C# - if I've got small, related classes, I'll often dump them into the same file because they'll still be easy to find - in this case, I'd stick
FlyBehaivour
and its implementations in the same file because they're so small and tightly related. \$\endgroup\$Kai– Kai2013年08月05日 08:13:59 +00:00Commented Aug 5, 2013 at 8:13