fork download
  1. /*
  2.  * Design a Bird
  3.  *
  4.  * Problem
  5.  * Model Pigeon, Crow, Sparrow, Ostrich, Penguin.
  6.  * Penguin and Ostrich cannot fly.
  7.  * Crow and Sparrow fly the same way. Pigeon flies differently.
  8.  * Every bird eats and sings the same way.
  9.  *
  10.  * These are different ways people first design this. Not all of them work.
  11.  *
  12.  * 1. One Bird class + if (sparrow) / else if (penguin) in fly()
  13.  * Breaks SRP and OCP. Unreadable at 20 species. Tests pile into
  14.  * one file. Two people collide on the same class.
  15.  *
  16.  * 2. Abstract Bird with fly() on the parent
  17.  * Penguin then "implements" fly with an empty body, a log, or
  18.  * an exception. Callers still think every Bird can fly. Breaks LSP.
  19.  *
  20.  * 3. FlyableBird / NonFlyableBird extra parent classes
  21.  * Fine for one behavior. Add eat variants and the tree explodes
  22.  * into 2^n classes. Flying and eating are behaviors, not kinds of bird.
  23.  *
  24.  * 4. The design in this file (the one that works)
  25.  * Bird holds identity + eat/sing. No fly() on Bird.
  26.  * Only flyers implement Flyable (ISP + LSP).
  27.  * Flying style is a separate class, injected in the constructor
  28.  * (Strategy + DIP + DI).
  29.  * Crow and Sparrow share CrowSparrowFlyingBehavior.
  30.  * A new bird is a new class. If it flies like Crow, reuse that
  31.  * behavior. Do not edit old species (OCP).
  32.  *
  33.  * Usage
  34.  * List<Bird> → describe / eat / sing for everyone
  35.  * List<Flyable> → fly() with no instanceof and no try/catch
  36.  */
  37.  
  38. import java.util.ArrayList;
  39. import java.util.List;
  40.  
  41. /* Small interfaces so a bird only takes on behaviors it actually has. */
  42.  
  43. interface Flyable {
  44. void fly();
  45. }
  46.  
  47. interface FlyingBehavior {
  48. void makeFly();
  49. }
  50.  
  51. interface EatingBehavior {
  52. void eat();
  53. }
  54.  
  55. interface SingingBehavior {
  56. void sing();
  57. }
  58.  
  59. /* Flying styles sit in their own classes so birds stay small. */
  60.  
  61. class CrowSparrowFlyingBehavior implements FlyingBehavior {
  62. @Override
  63. public void makeFly() {
  64. System.out.println(" flying with short rapid wing flaps");
  65. }
  66. }
  67.  
  68. class PigeonFlyingBehavior implements FlyingBehavior {
  69. @Override
  70. public void makeFly() {
  71. System.out.println(" flying in a steady glide with occasional flaps");
  72. }
  73. }
  74.  
  75. class DefaultEatingBehavior implements EatingBehavior {
  76. @Override
  77. public void eat() {
  78. System.out.println(" pecking food");
  79. }
  80. }
  81.  
  82. class DefaultSingingBehavior implements SingingBehavior {
  83. @Override
  84. public void sing() {
  85. System.out.println(" singing");
  86. }
  87. }
  88.  
  89. /*
  90.  * Shared name / color / size / weight, plus eat and sing.
  91.  * fly() is not here — Penguin and Ostrich would otherwise have to fake it.
  92.  */
  93. abstract class Bird {
  94. private final String name;
  95. private final String color;
  96. private final String size;
  97. private final double weightKg;
  98. private final EatingBehavior eating;
  99. private final SingingBehavior singing;
  100.  
  101. protected Bird(String name, String color, String size, double weightKg,
  102. EatingBehavior eating, SingingBehavior singing) {
  103. this.name = name;
  104. this.color = color;
  105. this.size = size;
  106. this.weightKg = weightKg;
  107. this.eating = eating;
  108. this.singing = singing;
  109. }
  110.  
  111. public String getName() { return name; }
  112.  
  113. public void eat() { eating.eat(); }
  114. public void sing() { singing.sing(); }
  115.  
  116. public void describe() {
  117. System.out.println(name + " [" + color + ", " + size + ", " + weightKg + "kg]");
  118. }
  119. }
  120.  
  121. /* Only species that fly implement Flyable. */
  122.  
  123. class Pigeon extends Bird implements Flyable {
  124. private final FlyingBehavior flying;
  125.  
  126. public Pigeon(String color, String size, double weightKg,
  127. EatingBehavior eating, SingingBehavior singing,
  128. FlyingBehavior flying) {
  129. super("Pigeon", color, size, weightKg, eating, singing);
  130. this.flying = flying;
  131. }
  132.  
  133. @Override
  134. public void fly() {
  135. System.out.print(getName());
  136. flying.makeFly();
  137. }
  138. }
  139.  
  140. class Crow extends Bird implements Flyable {
  141. private final FlyingBehavior flying;
  142.  
  143. public Crow(String color, String size, double weightKg,
  144. EatingBehavior eating, SingingBehavior singing,
  145. FlyingBehavior flying) {
  146. super("Crow", color, size, weightKg, eating, singing);
  147. this.flying = flying;
  148. }
  149.  
  150. @Override
  151. public void fly() {
  152. System.out.print(getName());
  153. flying.makeFly();
  154. }
  155. }
  156.  
  157. class Sparrow extends Bird implements Flyable {
  158. private final FlyingBehavior flying;
  159.  
  160. public Sparrow(String color, String size, double weightKg,
  161. EatingBehavior eating, SingingBehavior singing,
  162. FlyingBehavior flying) {
  163. super("Sparrow", color, size, weightKg, eating, singing);
  164. this.flying = flying;
  165. }
  166.  
  167. @Override
  168. public void fly() {
  169. System.out.print(getName());
  170. flying.makeFly();
  171. }
  172. }
  173.  
  174. class Ostrich extends Bird {
  175. public Ostrich(String color, String size, double weightKg,
  176. EatingBehavior eating, SingingBehavior singing) {
  177. super("Ostrich", color, size, weightKg, eating, singing);
  178. }
  179. }
  180.  
  181. class Penguin extends Bird {
  182. public Penguin(String color, String size, double weightKg,
  183. EatingBehavior eating, SingingBehavior singing) {
  184. super("Penguin", color, size, weightKg, eating, singing);
  185. }
  186. }
  187.  
  188. /* Main creates the behaviors and hands them to each bird. */
  189. public class Main {
  190. public static void main(String[] args) {
  191. EatingBehavior eat = new DefaultEatingBehavior();
  192. SingingBehavior sing = new DefaultSingingBehavior();
  193. FlyingBehavior crowSparrowFly = new CrowSparrowFlyingBehavior();
  194. FlyingBehavior pigeonFly = new PigeonFlyingBehavior();
  195.  
  196. Pigeon pigeon = new Pigeon("grey", "medium", 0.3, eat, sing, pigeonFly);
  197. Crow crow = new Crow("black", "medium", 0.5, eat, sing, crowSparrowFly);
  198. Sparrow sparrow = new Sparrow("brown", "small", 0.03, eat, sing, crowSparrowFly);
  199. Ostrich ostrich = new Ostrich("brown", "large", 110.0, eat, sing);
  200. Penguin penguin = new Penguin("black-white", "medium", 15.0, eat, sing);
  201.  
  202. List<Bird> sanctuary = new ArrayList<Bird>();
  203. sanctuary.add(pigeon);
  204. sanctuary.add(crow);
  205. sanctuary.add(sparrow);
  206. sanctuary.add(ostrich);
  207. sanctuary.add(penguin);
  208.  
  209. System.out.println("=== all birds eat + sing ===");
  210. for (Bird bird : sanctuary) {
  211. bird.describe();
  212. bird.eat();
  213. bird.sing();
  214. }
  215.  
  216. System.out.println();
  217. System.out.println("=== only Flyable birds fly ===");
  218. List<Flyable> flyers = new ArrayList<Flyable>();
  219. flyers.add(pigeon);
  220. flyers.add(crow);
  221. flyers.add(sparrow);
  222. for (Flyable flyer : flyers) {
  223. flyer.fly();
  224. }
  225.  
  226. System.out.println();
  227. System.out.println("Crow and Sparrow share CrowSparrowFlyingBehavior");
  228. System.out.println("Pigeon uses PigeonFlyingBehavior");
  229. System.out.println("Ostrich and Penguin are Birds, not Flyable");
  230. }
  231. }
Success #stdin #stdout 0.21s 60408KB
stdin
Standard input is empty
stdout
=== all birds eat + sing ===
Pigeon [grey, medium, 0.3kg]
  pecking food
  singing
Crow [black, medium, 0.5kg]
  pecking food
  singing
Sparrow [brown, small, 0.03kg]
  pecking food
  singing
Ostrich [brown, large, 110.0kg]
  pecking food
  singing
Penguin [black-white, medium, 15.0kg]
  pecking food
  singing

=== only Flyable birds fly ===
Pigeon  flying in a steady glide with occasional flaps
Crow  flying with short rapid wing flaps
Sparrow  flying with short rapid wing flaps

Crow and Sparrow share CrowSparrowFlyingBehavior
Pigeon uses PigeonFlyingBehavior
Ostrich and Penguin are Birds, not Flyable