(Inheritance and Polymorphism)//
protectedOCP:-0.1*
System.ObjectLSP: LiskovDIP: ISP: *
protectedOCP:-0.1
UsingInheritance.Calculator public int Add(int a, int b) { int result = a + b; return result;}public int Subtract(int a, int b){ int result = a - b; return result;}public int Multiply(int a, int b){ int result = a * b; return result;}
UsingInheritance.Program.Program.Main() (1/2)AdvancedCalculator calculator = new AdvancedCalculator();. . . switch (op){case 1: result = calculator.Add(operand1,operand2); Console.WriteLine("{0} + {1} = {2} ", operand1, operand2, result); break;. . .
UsingInheritance.Program.Program.Main() (2/2)case 5: functionValue = calculator.GetSine(angle); Console.WriteLine( "Sine of {0} (deg) = {1}", angle, functionValue); break;. . .}
UsingInheritance.AdvancedCalculator public class AdvancedCalculator : Calculator{ public double GetSine(double angle) { angle *= Math.PI / 180.0; return Math.Sin(angle); } . . .}
UML
class A {private int data1;private int data2; //other members are methods}
class B : A {private int data3; //other members are methods}
class C : B {private int data1;private int data4; //other members are methods}
A a = new A();B b = new B();C c = new C();data1data2data1data2data3abdata1data2data3data1data4c
protectedOCP:-0.1
UsingProtected.Program.Program.Main()DC d = new DC();d.SetX(3);//Console.WriteLine( d.GetX() ) ; // Error!//d.x = 77; // Error!d.Add2();
UsingProtected.Programclass BC { private int x; public void SetX( int x ) { this.x = x; } protected int GetX() { return x; }}class DC : BC { public void Add2() { int c = GetX(); SetX( c+2 ); } }
protectedOCP:-0.1
sealed class SClass {. . . . . .}
protectedOCP:-0.1
UsingConstructorsForInheritance.Program.Main() Animal slug = new Animal();Animal tweety = new Animal( "canary" );Primate godzilla = new Primate();Primate human = new Primate( 4 );Human jill = new Human();
UsingConstructorsForInheritance.Program (1/3)class Animal { private string species; public Animal() { Console.WriteLine("Animal()"); species = "Animal";}public Animal( string s ) { Console.WriteLine("Animal("+ s +")"); species = s; }}
UsingConstructorsForInheritance.Program(2/3)class Primate : Animal { private int heartCham; public Primate() : base() { Console.WriteLine( "Primate()" );}public Primate( int n ) : base( "Primate" ) { Console.WriteLine("Primate(" + n +")"); heartCham = n;}}
UsingConstructorsForInheritance.Program (3/3)class Human : Primate { public Human() : base( 4 ) { Console.WriteLine( "Human()" );}}
Primate human = new Primate( 4 );public Primate( int n ) : base( "Primate" ){ . . .
}public Animal( string s ){ . . . }
UsingConstructorsForInheritance
protectedOCP:-0.1
-(OCP:Open-Closed Principle)Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification*Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
OCPViolationExample
OCPViolationExample.Program.Program.Main() Point center;center.x = 15;center.y = 20;Point topLeft;topLeft.x = 30;topLeft.y = 40;Shape[] list = { new Circle(2, center), new Rectangle(3, 4, topLeft) };DrawAllShapes(list);
OCPViolationExample.Program.Program (1/2)static void DrawAllShapes(Shape[] list) { for (int i = 0; i < list.Length; ++i) { Shape s = list[i]; switch (s.type) { case ShapeType.CIRCLE: DrawCircle((Circle) s); break; case ShapeType.RECTANGLE: DrawRectangle((Rectangle) s); break; } }}
OCPViolationExample.Program.Program (2/2)static void DrawCircle(Circle c){ Console.WriteLine("Draw a circle");}static void DrawRectangle(Rectangle r){ Console.WriteLine("Draw a rectangle");}
OCPViolationExample.Program (1/2)class Shape { public ShapeType type; public Shape(ShapeType t) { type = t; }}class Circle : Shape { private int radius; private Point center; public Circle(int radius, Point center) :base(ShapeType.CIRCLE) { this.radius = radius; this.center = center; }}
OCPViolationExample.Program (2/2)class Rectangle : Shape{ private int width; private int height; private Point topLeft; public Rectangle(int width, int height, Point topLeft) : base(ShapeType.RECTANGLE) { this.width = width; this.height = height; this.topLeft = topLeft; }}
OCPViolationExampleShape()switchShapeenumswitchProgramDraw
protectedOCP:-0.1
(packaging)(inheritance)(polymorphism)
(compile-time binding)(run-time binding)(static binding)(dynamic binding)(virtual)(override)
DrawingAllShapes
DrawingAllShapes.Program.Program.Main() (1/2)Shape[] list = new Shape[2];Point center;center.x = 15;center.y = 20;Point topLeft;topLeft.x = 30;topLeft.y = 40;Circle c = new Circle(2, center);Rectangle r = new Rectangle(3, 4, topLeft);Console.WriteLine(", ");Console.WriteLine("1: , ");Console.WriteLine("2: , ");int ans = int.Parse(Console.ReadLine());
DrawingAllShapes.Program.Program.Main() (2/2)switch (ans){ case 1: list[0] = c; list[1] = r; break; case 2: list[0] = r; list[1] = c; break; default: . . .}DrawAllShapes(list);
DrawingAllShapes.Program.Program static void DrawAllShapes(Shape[] list){ int i; for (i = 0; i < list.Length; ++i) { list[i].Draw(); }}
DrawingAllShapes.Program (1/3)class Shape{ public Shape() { } virtual public void Draw() { }}
DrawingAllShapes.Program (2/3)class Circle : Shape{ private int radius; private Point center; public Circle(int radius, Point center) { this.radius = radius; this.center = center; } override public void Draw() { Console.WriteLine("Draw a circle"); }}
DrawingAllShapes.Program (3/3)class Rectangle : Shape { private int width; private int height; private Point topLeft; public Rectangle(int width, int height, Point topLeft) { this.width = width; this.height = height; this.topLeft = topLeft; } override public void Draw() { Console.WriteLine("Draw a rectangle"); }}
DrawingAllShapesTriangle
protectedOCP:-0.1
BlackJack_0_1
BlackJack_0_1.BlackJackTest (1/2)public static bool Scenario1_OK(){ Card[] cards = { new Card(Suit.SPADE, 1), new Card(Suit.HEART, 11), new Card(Suit.DIAMOND, 10) }; Deck deck = new Deck(cards); Player player = new Player(); Dealer dealer = new Dealer();
BlackJack_0_1.BlackJackTest (2/2)player.SaveACard(deck.DealACard()); dealer.SaveACard(deck.DealACard()); player.SaveACard(deck.DealACard()); return( player.GetStatus() == Status.BLACK_JACK && dealer.GetStatus() == Status.PASS);}
BlackJack_0_1.Game (1/6)const int N_PLAYERS = 2;Deck deck;Player[] players = new Player[N_PLAYERS];public Game(){ players[0] = new Player("Jeng"); players[N_PLAYERS-1] = new Dealer();}
BlackJack_0_1.Game (2/6)private void Play() { int i; // for (i = 0; i < N_PLAYERS; ++i) { players[i].SaveACard( deck.DealACard()); players[i].Dump(); }
BlackJack_0_1.Game (3/6) // for (i=0; i < N_PLAYERS; ++i) { players[i].SaveACard( deck.DealACard()); players[i].Dump(); }
BlackJack_0_1.Game (5/6) // Player dealer = players[N_PLAYERS-1]; for(i=0; i= players[i].GetTotalPoints()) { Console.WriteLine( dealer.Name + ""+players[i].Name); } else { Console.WriteLine( players[i].Name+""+dealer.Name); } }}
BlackJack_0_1.Game (6/6)private bool IsBlackJackOrBurst( Player player) { bool isBlackJack = false; if (player.GetStatus()==Status.BLACK_JACK) { isBlackJack = true; Console.WriteLine(player.Name+ " BlackJack!!!"); } bool isBurst = false; if (player.GetStatus() == Status.BURST){ isBurst = true; Console.WriteLine(player.Name+" !!!"); } return (isBlackJack || isBurst); }
BlackJack_0_1.Player (1/5)private Card[] hand = new Card[11];private int nCards;private Status status;private int totalPoints;private string name;public Player(){ nCards = 0; name = "";}
BlackJack_0_1.Player (2/5)public Player(string name){ nCards = 0; this.name = name;}public string Name{ get { return name; }}
BlackJack_0_1.Player (3/5)virtual public bool WantOneMoreCard(){ Console.Write("? (y/n) "); string answer = Console.ReadLine(); return (answer == "Y" || answer == "y");}
BlackJack_0_1.Player (4/5)public void Dump(){ int i; Console.Write(name+" : "); for (i = 0; i < nCards; ++i) { hand[i].Dump(); Console.Write("\t"); if ((i + 1) % 5 == 0) Console.WriteLine(); }
BlackJack_0_1.Player (5/5) Console.WriteLine(); Console.WriteLine(name + " : " + totalPoints);}
BlackJack_0_1.Dealerclass Dealer : Player { public Dealer() : base("") {} override public bool WantOneMoreCard() { return (base.GetTotalPoints() < 17); }}
protectedOCP:-0.1
UsingBase.Program
UsingBase.Program (1/3)// Define the base classclass Car{ public virtual void DescribeCar() { System.Console.WriteLine( "Four wheels and an engine."); }}
UsingBase.Program (2/3)// Define the derived classesclass ConvertibleCar : Car{ public new virtual void DescribeCar() { base.DescribeCar(); Console.WriteLine("A roof that opens up."); }}
UsingBase.Program (3/3)class Minivan : Car{ public override void DescribeCar() { base.DescribeCar(); Console.WriteLine( "Carries seven people."); }}
UsingBase.Program.Program.Main() (1/2)// new and override make no differences hereCar car1 = new Car();car1.DescribeCar();Console.WriteLine("----------");
ConvertibleCar car2 = new ConvertibleCar();car2.DescribeCar();Console.WriteLine("----------");
Minivan car3 = new Minivan();car3.DescribeCar();Console.WriteLine("----------");
UsingBase.Program.Program.Main() (2/2)// they are different in polymorphysm Car[] cars = new Car[3];cars[0] = new Car();cars[1] = new ConvertibleCar();cars[2] = new Minivan();foreach (Car vehicle in cars){ Console.WriteLine("Car object: " + vehicle.GetType()); vehicle.DescribeCar(); Console.WriteLine("----------");}
: overridePolymorphism ()
: new
UsingBase
System.ObjectLSP: LiskovDIP: ISP: *
System.ObjectEqualsGetHashCodeGetTypeReferenceEqualsToStringFinalize
InheritingObject.Program.Program.Main()Test t1 = new Test();Test t2 = new Test();bool isEqual = t1.Equals(t2);Console.WriteLine(t1.ToString());Console.WriteLine("t1 t2 " + isEqual);
InheritingObject.Program class Test{override public string ToString() { return "InheritingObject.Test"; }}
Boxing Unboxingint x = 10;Object obj = (Object) x; // boxingobj = 20;int j = (int)obj; // unboxing
System.ObjectLSP: LiskovDIP: ISP: *
Liskov (LSP: Liskov Substitution Principle)Subtype must be substitutable for their base typesSo1To2TPPo1o2LSPOCP*Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
IS-A
LSPViolationExample.Program (1/4)class Rectangle{ private int width; private int height; virtual public int Width { set { width = value; } } virtual public int Height { set { height = value; } }
LSPViolationExample.Program (2/4) public int Area() { return width * height; }}
LSPViolationExample.Program (3/4)class Square : Rectangle{ override public int Width { set { base.Width = value; base.Height = value; } } override public int Height { set { base.Width = value; base.Height = value; } }}
LSPViolationExample.Program (4/4)class Program{ static void Main(string[] args) { Square s = new Square(); Test(s); } static void Test(Rectangle r) { r.Width = 5; r.Height = 4; Debug.Assert(r.Area() == 20); }}
Rectangle.WidthDebug.Assert( (width == value) && (height == old.height));LSP
System.ObjectLSP: LiskovDIP: ISP: *
AbstractClassExample.Program.Program.Main() double a = 5.0;Square sq = new Square(a);Console.WriteLine("sq" + sq.Area());Circle c = new Circle(a);Console.WriteLine("c" + c.Area());
AbstractClassExample.Program (1/3)public abstract class Shape { private string shape; public Shape(string shape) { this.shape = shape; Console.WriteLine("" + shape); } abstract public double Area();}
AbstractClassExample.Program (2/3)public class Square : Shape{ double a; public Square(double a): base("") { this.a = a; } public override double Area() { return a * a; }}
AbstractClassExample.Program (3/3)public class Circle : Shape{ double r; public Circle(double r): base("") { this.r = r; } public override double Area() { return Math.PI * r * r; }}
DrawingAllShapesShapeShape.Draw()
System.ObjectLSP: LiskovDIP: ISP: *
(DIP: Dependency-Inversion Principle)High-level modules should not depend on low-level modules. Both should depend on abstractions.Abstractions should not depend on details. Details should depend on abstractions.*Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
DIP
(State Chart)
DIPViolationExample.Program (1/4)public enum LampStatus{ OFF = 0, ON = 1}public enum ButtonStatus{ RELEASED = 0, PRESSED = 1}
DIPViolationExample.Program (2/4)public class Lamp { private LampStatus status = LampStatus.OFF; public LampStatus Status { get { return status; } } public void TurnOn() { status = LampStatus.ON; } public void TurnOff() { status = LampStatus.OFF; } }
DIPViolationExample.Program (3/4)public class Button{ private ButtonStatus status = ButtonStatus.RELEASED; private Lamp lamp; public Button(Lamp lamp) { this.lamp = lamp; } public ButtonStatus Status { get { return status; } }
DIPViolationExample.Program (4/4) public void Press() { if (status == ButtonStatus.RELEASED) { status = ButtonStatus.PRESSED; lamp.TurnOn(); } } public void Release() { if( status == ButtonStatus.PRESSED ){ status = ButtonStatus.RELEASED; lamp.TurnOff(); } }}
DIPViolationExample.Program.Program.Main() (2/3) if (rand.Next() % 2 == 1) { if (button.Status == ButtonStatus.PRESSED) { button.Release(); } else { button.Press(); } }
DIPViolationExample.Program.Program.Main() (3/3) if (lamp1.Status == LampStatus.OFF) { Console.WriteLine("lamp1 is off"); } else { Console.WriteLine("lamp1 is on"); } Console.WriteLine();}
System.ObjectLSP: LiskovDIP: ISP: *
UsingInterface.Program.Program.Main() double a = 5.0;Square sq = new Square(a);Console.WriteLine("sq" + sq.Area());Circle c = new Circle(a);Console.WriteLine("c" + c.Area());
UsingInterface.Program(1/3)interface Shape{ double Area();}
UsingInterface.Program(2/3)public class Square : Shape{ double a; public Square(double a) { this.a = a; } public double Area() { return a * a; }}
UsingInterface.Program(3/3)public class Circle : Shape{ double r; public Circle(double r) { this.r = r; } public double Area() { return Math.PI * r * r; }}
vs. (1/2)interface Shape{ double Area();}--------------------------------------------public abstract class Shape { private string shape; public Shape(string shape) { this.shape = shape; Console.WriteLine("" + shape); } abstract public double Area();}
vs. (2/2)public class Square : Shape {. . . public double Area() { return a * a; }} --------------------------------------------public class Square : Shape { . . . public override double Area() { return a * a; }}
DIP
ButtonAndLamp.Program (1/4)public interface SwitchableDevice { void TurnOn(); void TurnOff();}public enum LampStatus { OFF = 0, ON = 1}public enum ButtonStatus { RELEASED = 0, PRESSED = 1}
ButtonAndLamp.Program (2/4)public class Lamp : SwitchableDevice { private LampStatus status = LampStatus.OFF; public LampStatus Status { get { return status; } } public void TurnOn() { status = LampStatus.ON; } public void TurnOff() { status = LampStatus.OFF; }}
ButtonAndLamp.Program (3/4)public class Button{ private ButtonStatus status = ButtonStatus.RELEASED; private SwitchableDevice device; public Button(SwitchableDevice device) { this.device = device; } public ButtonStatus Status { get { return status; } }
ButtonAndLamp.Program (4/4) public void Press() { if (status == ButtonStatus.RELEASED) { status = ButtonStatus.PRESSED; device.TurnOn(); } } public void Release() { if (status == ButtonStatus.PRESSED) { status = ButtonStatus.RELEASED; device.TurnOff(); } }}
ButtonAndLampFanButtonFan
System.ObjectLSP: LiskovDIP: ISP: *
(ISP: Interface-Segregation Principle)Clients should not be forced to depend on methods that they do not use
ISP
ISPViolationExample.Program (1/4)interface TimerClient { void TimeOut();}interface Door : TimerClient { void Lock(); void Unlock(); bool IsOpen();}enum DoorStatus { CLOSED = 0, OPEN = 1}
ISPViolationExample.Program (2/4)class Timer { private int t; private int timeout; private TimerClient client; public Timer(int timeout, TimerClient client) { this.timeout = timeout; this.client = client; t = 0; }
ISPViolationExample.Program (3/4) public void Advance() { ++t; if (t % timeout == 0) { client.TimeOut(); } }}class TimedDoor : Door{ private DoorStatus status = DoorStatus.CLOSED;
ISPViolationExample.Program (4/4) public bool IsOpen() { return (status == DoorStatus.OPEN); } public void Lock() { if (IsOpen()) status = DoorStatus.CLOSED; } public void Unlock() { if (!IsOpen()) status = DoorStatus.OPEN; } public void TimeOut() { Lock(); }}
ISPViolationExample.Program.Program.Main() (2/2) if (tDoor.IsOpen()) { Console.WriteLine( "n = " + n + "\t tDoor is open"); } else { Console.WriteLine( "n = " + n + "\t tDoor is closed"); } timer.Advance();}
System.ObjectLSP: LiskovDIP: ISP: *
MultiInterface
MultiInterface.Program.Program.Main()Floatplane fp = new Floatplane();fp.Sail();fp.Fly();
MultiInterface.Program (1/2)interface Plane{ void Fly();}
interface Ship{ void Sail();}
MultiInterface.Program (2/2)public class Floatplane : Plane, Ship { public Floatplane() { Console.WriteLine(""); } public void Sail() { Console.WriteLine(""); } public void Fly() { Console.WriteLine(""); }}
Clock_RadioClockGetTime()RadioPlayMusic()
ISP
TimedDoorSimulation.Programinterface TimerClient { void TimeOut();}interface Door { void Lock(); void Unlock(); bool IsOpen();}. . .class TimedDoor : Door, TimerClient{ . . .}
System.ObjectLSP: LiskovDIP: ISP: *
CastMultiInterfaces
CastMultiInterfaces.Program.Program.Main()double a = 5.0;Square sq = new Square(a);Rhombus rhomb = sq as Rhombus;Console.WriteLine( "sq"+rhomb.Area() );if( sq is Rectangle ) { Rectangle rec = (Rectangle) sq; Console.WriteLine("sq"+rec.Area() );}
CastMultiInterfaces.Program(1/3)interface Rectangle{ double Area();}interface Rhombus{ double Area();}
CastMultiInterfaces.Program(2/3)public class Square : Rectangle, Rhombus{ private double a; private double d; public Square(double a) { this.a = a; d = Math.Sqrt(2.0) * a; }
CastMultiInterfaces.Program(3/3)double Rectangle.Area() { return a * a; } double Rhombus.Area() { return 0.5 * d * d; }}