├── .gitignore ├── src ├── koan │ ├── resources │ │ ├── KoanBasics.problem │ │ ├── StringConcatenation.solution │ │ ├── GenericsBasics.solution │ │ ├── PrimitiveBasics.problem │ │ ├── InputStreamDecoratorKoan.solution │ │ ├── OverloadingBasics.solution │ │ ├── TypeErasure.solution │ │ ├── KoanBasics.solution │ │ ├── VarargsBasics.solution │ │ ├── PieDecoratorKoan │ │ ├── InvariantBasics.solution │ │ ├── FunWithFloats.solution │ │ ├── Autoboxing.solution │ │ ├── Overriding.solution │ │ ├── ObserverKoan.solution │ │ └── PrimitiveBasics.solution │ └── java │ │ └── com │ │ └── javakoan │ │ └── lesson │ │ ├── java │ │ ├── generics │ │ │ ├── GenericsBasics.java │ │ │ └── TypeErasure.java │ │ ├── strings │ │ │ └── StringConcatenation.java │ │ ├── primer │ │ │ ├── KoanBasics.java │ │ │ └── JUnitBasics.java │ │ ├── overloading │ │ │ └── OverloadingBasics.java │ │ ├── invariants │ │ │ └── InvariantBasics.java │ │ ├── primitives │ │ │ ├── Autoboxing.java │ │ │ ├── FunWithFloats.java │ │ │ └── PrimitiveBasics.java │ │ ├── overriding │ │ │ └── Overriding.java │ │ └── varargs │ │ │ └── VarargsBasics.java │ │ └── patterns │ │ ├── decorator │ │ ├── ex2 │ │ │ └── InputStreamDecoratorKoan.java │ │ └── ex1 │ │ │ └── PieDecoratorKoan.java │ │ ├── observer │ │ ├── ex2 │ │ │ └── NewsKioskTest.java │ │ └── ex1 │ │ │ └── ObserverKoan.java │ │ └── strategy │ │ └── ex1 │ │ └── NewsKioskTest.java └── main │ └── java │ └── com │ └── javakoan │ └── lesson │ ├── java │ ├── overriding │ │ ├── SantasLittleHelper.java │ │ ├── Walker.java │ │ ├── Beagle.java │ │ ├── Cartoon.java │ │ ├── Dog.java │ │ ├── ModernCartoon.java │ │ ├── Greyhound.java │ │ └── Snoopy.java │ ├── varargs │ │ ├── Compute.java │ │ ├── BrokenCompute.java │ │ ├── ImprovedCompute.java │ │ └── PerformantCompute.java │ ├── mutables │ │ ├── BrokenPeriod.java │ │ └── Period.java │ ├── overloading │ │ └── DogClassifier.java │ └── primitives │ │ └── SweetShop.java │ └── patterns │ ├── factory │ ├── domain │ │ ├── Pie.java │ │ ├── PotPie.java │ │ └── SweenieToddPie.java │ └── ex1 │ │ ├── PotPieFactory.java │ │ ├── SweenieToddPieFactory.java │ │ └── PieFactory.java │ ├── decorator │ ├── domain │ │ ├── PieDecorator.java │ │ ├── ChickenAndLeek.java │ │ ├── SpinachAndFeta.java │ │ ├── SteakAndKidney.java │ │ ├── Mash.java │ │ ├── Liquor.java │ │ └── Pie.java │ └── ex2 │ │ └── L33tSp34kInputStream.java │ ├── observer │ ├── ex2 │ │ ├── Observer.java │ │ ├── Subject.java │ │ ├── HomeDeliveryCustomer.java │ │ └── NewsKiosk.java │ ├── domain │ │ ├── Publication.java │ │ ├── Customer.java │ │ └── Magazine.java │ └── ex1 │ │ ├── NewsKiosk.java │ │ ├── HomeDeliveryCustomer.java │ │ └── CollectionCustomer.java │ └── strategy │ ├── ex1 │ ├── Observer.java │ ├── AcquirementBehavior.java │ ├── Subject.java │ ├── AcquirementByDelivery.java │ ├── AcquirementByCollection.java │ └── NewsKiosk.java │ └── domain │ ├── Publication.java │ ├── Magazine.java │ └── Customer.java ├── .github └── workflows │ └── maven-package.yml ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target/ 3 | java-koans.iml -------------------------------------------------------------------------------- /src/koan/resources/KoanBasics.problem: -------------------------------------------------------------------------------- 1 | reflectOnProvidingTheStartingProblemForAKoan 2 | [ 3 | Math.max(i, j); 4 | ] 5 | -------------------------------------------------------------------------------- /src/koan/resources/StringConcatenation.solution: -------------------------------------------------------------------------------- 1 | meditateOnTheUseOfStringBuilder 2 | [ 3 | for(int i = 0; i < 50; i++){ 4 | result += sampleString; 5 | } 6 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/SantasLittleHelper.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public class SantasLittleHelper extends Greyhound implements ModernCartoon { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/domain/Pie.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.domain; 2 | 3 | /** 4 | * Created by nicholas_smith on 26/11/13. 5 | */ 6 | public abstract class Pie { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/domain/PotPie.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.domain; 2 | 3 | /** 4 | * Created by nicholas_smith on 26/11/13. 5 | */ 6 | public class PotPie extends Pie { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Walker.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | 4 | public interface Walker { 5 | 6 | default String walkies() { 7 | return "wag-tail"; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/domain/SweenieToddPie.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.domain; 2 | 3 | /** 4 | * Created by nicholas_smith on 26/11/13. 5 | */ 6 | public class SweenieToddPie extends Pie { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Beagle.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public class Beagle extends Dog{ 4 | 5 | @Override 6 | public String bark() { 7 | return "yap-yap" ; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/koan/resources/GenericsBasics.solution: -------------------------------------------------------------------------------- 1 | reflectOnCompileTimeSafetyOfGenerics 2 | [ 3 | try { 4 | element = (String) strings.get(1); 5 | } catch (ClassCastException e){ 6 | exceptionMessage = e.getMessage(); 7 | } 8 | ] -------------------------------------------------------------------------------- /src/koan/resources/PrimitiveBasics.problem: -------------------------------------------------------------------------------- 1 | reflectOnNullPointersWhenUnboxingIntegers 2 | [ 3 | fail("Should have thrown null pointer"); 4 | ] 5 | 6 | reflectOnSaferToFavouringAPrimitive 7 | [ 8 | fail("Uninitialised int should be zero"); 9 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Cartoon.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public interface Cartoon { 4 | 5 | default String drawnBy(){ 6 | return "hand"; 7 | } 8 | 9 | String firstAppearance(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Dog.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public class Dog implements Walker { 4 | 5 | public static String rollOver(){ 6 | return "rolls-over"; 7 | } 8 | 9 | public String bark() { 10 | return "woof-woof" ; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/ModernCartoon.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public interface ModernCartoon extends Cartoon { 4 | 5 | default String drawnBy(){ 6 | return "computer"; 7 | } 8 | 9 | default String firstAppearance(){ 10 | return "on tv"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Greyhound.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | 4 | public class Greyhound extends Dog { 5 | 6 | @Override 7 | public String walkies() { 8 | return "run-about-the-house"; 9 | } 10 | 11 | @Override 12 | public String bark() { 13 | return "howl" ; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/koan/resources/InputStreamDecoratorKoan.solution: -------------------------------------------------------------------------------- 1 | reflectOnTheDecoratorPatternBeingUsingInTheCoreJavaLibraries 2 | [ 3 | int character; 4 | StringBuilder sb = new StringBuilder(); 5 | while((character = in.read()) >= 0){ 6 | sb.append((char)character); 7 | } 8 | phraseUnderMeditation = sb.toString(); 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /src/koan/resources/OverloadingBasics.solution: -------------------------------------------------------------------------------- 1 | meditateThatOverloadingIsAssessedAtCompileTime 2 | [ 3 | secondDogClassification = "It's just some mongrel"; 4 | thirdDogClassification = "It's just some mongrel"; 5 | ] 6 | 7 | meditateOnHowToResolveOverloadingConfusion 8 | [ 9 | secondDogClassification = "It's a Beagle"; 10 | thirdDogClassification = "It's Snoopy!"; 11 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/ex1/PotPieFactory.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.ex1; 2 | 3 | import com.javakoan.lesson.patterns.factory.domain.Pie; 4 | 5 | /** 6 | * Created by nicholas_smith on 26/11/13. 7 | */ 8 | public class PotPieFactory extends PieFactory { 9 | 10 | 11 | @Override 12 | protected Pie createPie() { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/koan/resources/TypeErasure.solution: -------------------------------------------------------------------------------- 1 | reflectOnAccessingAGenericizedType 2 | [ 3 | firstElement = strings.get(0); 4 | secondElement = strings.get(1); 5 | thirdElement = strings.get(2); 6 | ] 7 | 8 | reflectOnWhatTheAboveCodeCompilesAs 9 | [ 10 | firstElement = (String) strings.get(0); 11 | secondElement = (String) strings.get(1); 12 | thirdElement = (String) strings.get(2); 13 | ] 14 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/ex1/SweenieToddPieFactory.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.ex1; 2 | 3 | import com.javakoan.lesson.patterns.factory.domain.Pie; 4 | 5 | /** 6 | * Created by nicholas_smith on 26/11/13. 7 | */ 8 | public class SweenieToddPieFactory extends PieFactory { 9 | 10 | 11 | @Override 12 | protected Pie createPie() { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/koan/resources/KoanBasics.solution: -------------------------------------------------------------------------------- 1 | reflectOnTheProductOfIAndJ 2 | [ 3 | product = i * j; 4 | ] 5 | 6 | reflectOnProvidingTheSolutionToTheProductOfIAndJ 7 | [ 8 | product = i * j; 9 | ] 10 | 11 | reflectOnProvidingTheStartingProblemForAKoan 12 | [ 13 | largest = Math.max(i, j); 14 | ] 15 | 16 | reflectOneIgnoringWhenVexedAndEnlightened 17 | [ 18 | fail("This Koan should be ignored as it is both Vexed and Enlightened"); 19 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/PieDecorator.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 11:00 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public abstract class PieDecorator extends Pie { 11 | 12 | protected Pie pie; 13 | 14 | public abstract String getDescription(); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex2/Observer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex2; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 14:41 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface Observer { 13 | 14 | public void update(Publication publication); 15 | } 16 | -------------------------------------------------------------------------------- /src/koan/resources/VarargsBasics.solution: -------------------------------------------------------------------------------- 1 | reflectOnCallingAMethodThatHasAVariableNumberOfArguments 2 | [ 3 | maxUnderMeditation = 8; 4 | ] 5 | 6 | reflectOnHowVarargsCanIntroduceRuntimeExceptions 7 | [ 8 | BrokenCompute.max(); 9 | ] 10 | 11 | reflectOnAddingExceptionHandlingForZeroArgument 12 | [ 13 | messageUnderMeditation = "Too few arguments"; 14 | ] 15 | 16 | reflectOnCallingAMethodThatRequiresAtLeastOneArgument 17 | [ 18 | maxUnderMeditation = 8; 19 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/Observer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 14:41 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface Observer { 13 | 14 | public void update(Subject subject, Publication publication); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/factory/ex1/PieFactory.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.factory.ex1; 2 | 3 | import com.javakoan.lesson.patterns.factory.domain.Pie; 4 | 5 | /** 6 | * Created by nicholas_smith on 26/11/13. 7 | */ 8 | public abstract class PieFactory { 9 | 10 | 11 | 12 | public Pie orderPie(){ 13 | 14 | return createPie(); 15 | } 16 | 17 | protected abstract Pie createPie(); 18 | 19 | public static enum PIE{ 20 | POT, 21 | SWEENIE_TODD 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/AcquirementBehavior.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 15:46 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface AcquirementBehavior { 13 | 14 | public Publication acquire(Subject subject, Publication publication); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/ChickenAndLeek.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 10:57 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class ChickenAndLeek extends Pie { 11 | 12 | public ChickenAndLeek(){ 13 | description = "A Chicken and Leek Pie"; 14 | } 15 | 16 | @Override 17 | public int cost() { 18 | return 250; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/SpinachAndFeta.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 10:59 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class SpinachAndFeta extends Pie { 11 | 12 | public SpinachAndFeta(){ 13 | description = "A Spinach and Feta Pie"; 14 | } 15 | 16 | @Override 17 | public int cost() { 18 | return 200; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/SteakAndKidney.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 10:54 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class SteakAndKidney extends Pie{ 11 | 12 | public SteakAndKidney(){ 13 | description = "Classic Steak and Kidney Pie"; 14 | } 15 | 16 | @Override 17 | public int cost() { 18 | return 300; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/domain/Publication.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/07/13 7 | * Time: 11:11 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public abstract class Publication { 11 | 12 | private String content; 13 | 14 | protected Publication(String content) { 15 | this.content = content; 16 | } 17 | 18 | public String getContent() { 19 | return content; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/domain/Publication.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/07/13 7 | * Time: 11:11 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public abstract class Publication { 11 | 12 | private String content; 13 | 14 | protected Publication(String content) { 15 | this.content = content; 16 | } 17 | 18 | public String getContent() { 19 | return content; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/koan/resources/PieDecoratorKoan: -------------------------------------------------------------------------------- 1 | reflectOnThePriceOfAChickenAndLeekPieWithMash 2 | [ 3 | pie = new Mash(pie); 4 | ] 5 | 6 | reflectOnTheDescriptionOfAChickenAndLeekPieWithMash 7 | [ 8 | descriptionUnderMeditation = "A Chicken and Leek Pie, Liquor"; 9 | ] 10 | 11 | reflectOneUsingAddingTheSameDecorationTwice 12 | [ 13 | pie = new Mash(pie); 14 | pie = new Mash(pie); 15 | pie = new Liquor(pie); 16 | ] 17 | 18 | reflectOnTheDecorationOrderBeingImportant 19 | [ 20 | descriptionUnderMeditation = "Classic Steak and Kidney Pie, Mash, Liquor, Mash"; 21 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex2/Subject.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex2; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 14:41 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface Subject { 13 | 14 | public void register(Observer observer); 15 | public void remove(Observer observer); 16 | public void notifyObservers(Publication publication); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/Subject.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 14:41 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public interface Subject { 13 | 14 | public void register(Observer observer); 15 | public void remove(Observer observer); 16 | public void notifyObservers(Publication publication); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/varargs/Compute.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.varargs; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 30/05/13 7 | * Time: 10:20 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Compute { 11 | 12 | public static int max(int firstArg, int... remainingArgs) { 13 | int max = firstArg; 14 | for (int arg : remainingArgs) { 15 | if (arg > max) { 16 | max = arg; 17 | } 18 | } 19 | return max; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/varargs/BrokenCompute.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.varargs; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 30/05/13 7 | * Time: 09:53 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class BrokenCompute { 11 | 12 | public static int max(int ... args) { 13 | int max = args[0]; 14 | for (int i = 1; i < args.length; i++) { 15 | if (args[i] > max){ 16 | max = args[i]; 17 | } 18 | } 19 | return max; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/AcquirementByDelivery.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 15:57 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class AcquirementByDelivery implements AcquirementBehavior { 13 | 14 | @Override 15 | public Publication acquire(Subject subject, Publication publication) { 16 | return publication; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overriding/Snoopy.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | public class Snoopy extends Beagle implements Cartoon { 4 | 5 | private static final Snoopy INSTANCE = new Snoopy(); 6 | 7 | private Snoopy(){} 8 | 9 | public static String rollOver(){ 10 | return "unlikely"; 11 | } 12 | 13 | @Override 14 | public String bark() { 15 | return "peanuts" ; 16 | } 17 | 18 | @Override 19 | public String firstAppearance() { 20 | return "in print"; 21 | } 22 | 23 | public static Snoopy getInstance(){ 24 | return INSTANCE; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/domain/Customer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/07/13 7 | * Time: 11:23 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Customer { 11 | 12 | protected String name; 13 | 14 | public Customer(String name) { 15 | this.name = name; 16 | } 17 | 18 | public void read(Publication publication){ 19 | System.out.print(name + " is reading: "); 20 | System.out.println(publication.getContent()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/Mash.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 11:01 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Mash extends PieDecorator { 11 | 12 | public Mash(Pie pie){ 13 | this.pie = pie; 14 | } 15 | 16 | @Override 17 | public int cost() { 18 | return pie.cost() + 100; 19 | } 20 | 21 | @Override 22 | public String getDescription() { 23 | return pie.getDescription() + ", Mash"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/Liquor.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 11:14 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Liquor extends PieDecorator { 11 | 12 | public Liquor(Pie pie){ 13 | this.pie = pie; 14 | } 15 | 16 | @Override 17 | public int cost() { 18 | return pie.cost() + 50; 19 | } 20 | 21 | @Override 22 | public String getDescription() { 23 | return pie.getDescription() + ", Liquor"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/domain/Pie.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/08/13 7 | * Time: 10:53 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public abstract class Pie { 11 | 12 | protected String description = "Pie Shell No Filling"; 13 | 14 | public abstract int cost(); 15 | 16 | public String getDescription() { 17 | return description; 18 | } 19 | 20 | public void setDescription(String description) { 21 | this.description = description; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/maven-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path 3 | 4 | name: Java CI 5 | 6 | on: [push] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up JDK 17 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: '17' 18 | distribution: 'temurin' 19 | - name: Build with Maven 20 | run: mvn --batch-mode --update-snapshots package -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/domain/Magazine.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/07/13 7 | * Time: 11:06 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Magazine extends Publication { 11 | 12 | private int edition; 13 | 14 | public Magazine(String content, int edition) { 15 | super(content); 16 | this.edition = edition; 17 | } 18 | 19 | public int getEdition() { 20 | return edition; 21 | } 22 | 23 | public void setEdition(int edition) { 24 | this.edition = edition; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/domain/Magazine.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.domain; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 08/07/13 7 | * Time: 11:06 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class Magazine extends Publication { 11 | 12 | private int edition; 13 | 14 | public Magazine(String content, int edition) { 15 | super(content); 16 | this.edition = edition; 17 | } 18 | 19 | public int getEdition() { 20 | return edition; 21 | } 22 | 23 | public void setEdition(int edition) { 24 | this.edition = edition; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/koan/resources/InvariantBasics.solution: -------------------------------------------------------------------------------- 1 | reflectOnUsingConstructorsToPreserveInvariants 2 | [ 3 | messageUnderMeditation = "Start Must Be Before End"; 4 | ] 5 | 6 | reflectOnBreakingThePeriodByMakingEndBeforeStart 7 | [ 8 | end.setTime(APRIL_1_2013.getTime()); 9 | ] 10 | 11 | reflectAgainOnCompromisingThePeriodByMakingEndBeforeStart 12 | [ 13 | brokenPeriod.getEnd().setTime(APRIL_1_2013.getTime()); 14 | ] 15 | 16 | reflectOnUsingDefensiveCopiesToPreserveClassInvariants 17 | [ 18 | startUnderMeditation = period.getStart(); 19 | endUnderMeditation = period.getEnd(); 20 | ] 21 | 22 | reflectOnHowTheJodaLibraryIsBetterThanJavaUtilDate 23 | [ 24 | messageUnderMeditation = "Start Must Be Before End"; 25 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/varargs/ImprovedCompute.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.varargs; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 30/05/13 7 | * Time: 10:20 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class ImprovedCompute { 11 | 12 | public static int max(int... args) { 13 | if (args.length == 0) { 14 | throw new IllegalArgumentException("Too few arguments"); 15 | } 16 | int max = args[0]; 17 | for (int i = 1; i < args.length; i++) { 18 | if (args[i] > max){ 19 | max = args[i]; 20 | } 21 | } 22 | return max; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/AcquirementByCollection.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 08/07/13 9 | * Time: 15:52 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class AcquirementByCollection implements AcquirementBehavior { 13 | 14 | @Override 15 | public Publication acquire(Subject subject, Publication publication) { 16 | if(subject instanceof NewsKiosk){ 17 | return ((NewsKiosk)subject).getLatestMagazine(); 18 | } else { 19 | throw new IllegalArgumentException("Unknown Subject Type"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/koan/resources/FunWithFloats.solution: -------------------------------------------------------------------------------- 1 | reflectOnProducingTheStringValue100000 2 | [ 3 | stringValue = String.valueOf(f*10); 4 | ] 5 | 6 | reflectOnProducingTheStringValueInfinity 7 | [ 8 | stringValue = String.valueOf(d * 10); 9 | ] 10 | 11 | reflectOnProducingTheStringValueNaN 12 | [ 13 | stringValue = String.valueOf(d / d); 14 | ] 15 | 16 | reflectOnHowAnArithmeticExceptionCanOccur 17 | [ 18 | toCauseException = toCauseException / 0; 19 | ] 20 | 21 | reflectOnFloatBeingAPoorChoiceToRepresentCurrency 22 | [ 23 | expectedNumberOfSweets = 3; 24 | ] 25 | 26 | reflectOnBigDecimalBeingABetterChoiceForCurrency 27 | [ 28 | expectedNumberOfSweets = 4; 29 | ] 30 | 31 | reflectOnIntBeingABetterAndMoreEfficientChoiceForCurrency 32 | [ 33 | expectedNumberOfSweets = 4; 34 | ] -------------------------------------------------------------------------------- /src/koan/resources/Autoboxing.solution: -------------------------------------------------------------------------------- 1 | reflectedOnAutoboxingWithAssignment 2 | [ 3 | i = 5; 4 | ] 5 | 6 | reflectedOnAutoboxingWhenPassingParametersWhileCallingSquareOf 7 | [ 8 | i = Boxer.squareOf(5); 9 | ] 10 | 11 | reflectedOnUnboxingWithAssignment 12 | [ 13 | i = new Integer(5); 14 | ] 15 | 16 | reflectedOnUnboxingWithAssignmentInALoop 17 | [ 18 | sum += i; 19 | ] 20 | 21 | reflectedOnUnboxingWhenPassingParametersWhileCallingDoubleOf 22 | [ 23 | i = Boxer.doubleOf(new Integer(5)); 24 | ] 25 | 26 | reflectOnTheDifferencesBetweenCollectionImplementationsWhenRemovingItems 27 | [ 28 | expectedSetOutput = "[-3, -2, -1]"; 29 | expectedListOutput = "[-2, 0, 2]"; 30 | ] 31 | 32 | reflectOnHowBoxingOccursWithValueOfNotNew 33 | [ 34 | l3 = new Long(1L); 35 | l6 = l5; 36 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/mutables/BrokenPeriod.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.mutables; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 29/05/13 9 | * Time: 14:50 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class BrokenPeriod { 13 | 14 | private Date start; 15 | private Date end; 16 | 17 | public BrokenPeriod(Date start, Date end) { 18 | 19 | if(start.after(end)){ 20 | throw new IllegalArgumentException("Start Must Be Before End"); 21 | } 22 | 23 | this.start = start; 24 | this.end = end; 25 | } 26 | 27 | public Date getStart() { 28 | return start; 29 | } 30 | 31 | public Date getEnd() { 32 | return end; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/mutables/Period.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.mutables; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 29/05/13 9 | * Time: 15:11 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class Period { 13 | 14 | private Date start; 15 | private Date end; 16 | 17 | public Period(Date start, Date end) { 18 | 19 | this.start = new Date(start.getTime()); 20 | this.end = new Date(end.getTime()); 21 | 22 | if(start.after(end)){ 23 | throw new IllegalArgumentException("Start Must Be Before End"); 24 | } 25 | } 26 | 27 | public Date getStart() { 28 | return new Date(start.getTime()); 29 | } 30 | 31 | public Date getEnd() { 32 | return new Date(end.getTime()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex2/HomeDeliveryCustomer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex2; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Customer; 4 | import com.javakoan.lesson.patterns.observer.domain.Publication; 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: nicholas_smith 9 | * Date: 08/07/13 10 | * Time: 14:45 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | public class HomeDeliveryCustomer extends Customer implements Observer { 14 | 15 | private String address; 16 | 17 | private Subject subject; 18 | 19 | public HomeDeliveryCustomer(String name, Subject subject) { 20 | super(name); 21 | this.subject = subject; 22 | subject.register(this); 23 | } 24 | 25 | @Override 26 | public void update(Publication publication) { 27 | read(publication); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/decorator/ex2/L33tSp34kInputStream.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.ex2; 2 | 3 | import java.io.FilterInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: nicholas_smith 10 | * Date: 08/08/13 11 | * Time: 11:42 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | public class L33tSp34kInputStream extends FilterInputStream { 15 | 16 | public L33tSp34kInputStream(InputStream in) { 17 | super(in); 18 | } 19 | 20 | public int read() throws IOException { 21 | int character = super.read(); 22 | 23 | if((char)character == 'a' || (char)character == 'A'){ 24 | return '4'; 25 | } 26 | 27 | if((char)character == 'e' || (char)character == 'E'){ 28 | return '3'; 29 | } 30 | 31 | return character; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex1/NewsKiosk.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex1; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Magazine; 4 | 5 | import java.util.Observable; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: nicholas_smith 10 | * Date: 08/07/13 11 | * Time: 11:02 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | public class NewsKiosk extends Observable { 15 | 16 | private Magazine latestMagazine; 17 | 18 | 19 | public void newMagazinePublished(Magazine magazine){ 20 | 21 | this.latestMagazine = magazine; 22 | 23 | setChanged(); 24 | notifyObservers(magazine); 25 | } 26 | 27 | public void newMagazinePublishedCollectionOnly(Magazine magazine){ 28 | 29 | this.latestMagazine = magazine; 30 | 31 | setChanged(); 32 | notifyObservers(); 33 | } 34 | 35 | public Magazine getLatestMagazine(){ 36 | return latestMagazine; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex1/HomeDeliveryCustomer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex1; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Customer; 4 | import com.javakoan.lesson.patterns.observer.domain.Publication; 5 | 6 | import java.util.Observable; 7 | import java.util.Observer; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: nicholas_smith 12 | * Date: 08/07/13 13 | * Time: 11:09 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class HomeDeliveryCustomer extends Customer implements Observer { 17 | 18 | private String address; 19 | 20 | private Observable observable; 21 | 22 | public HomeDeliveryCustomer(String name, Observable observable) { 23 | super(name); 24 | this.observable = observable; 25 | observable.addObserver(this); 26 | } 27 | 28 | @Override 29 | public void update(Observable o, Object arg) { 30 | if(arg instanceof Publication){ 31 | read((Publication) arg); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/overloading/DogClassifier.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overloading; 2 | 3 | import com.javakoan.lesson.java.overriding.Beagle; 4 | import com.javakoan.lesson.java.overriding.Dog; 5 | import com.javakoan.lesson.java.overriding.Snoopy; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: nicholas_smith 10 | * Date: 29/05/13 11 | * Time: 16:36 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | public class DogClassifier { 15 | 16 | public static String classify(Beagle beagle) { 17 | return "It's a Beagle"; 18 | } 19 | 20 | public static String classify(Snoopy snoopy) { 21 | return "It's Snoopy!"; 22 | } 23 | 24 | public static String classify(Dog dog) { 25 | return "It's just some mongrel"; 26 | } 27 | 28 | 29 | public static String classifyAnyDog(Dog dog) { 30 | return dog instanceof Snoopy ? "It's Snoopy!" : 31 | dog instanceof Beagle ? "It's a Beagle" : 32 | "It's just some mongrel"; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex1/CollectionCustomer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex1; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Customer; 4 | import com.javakoan.lesson.patterns.observer.domain.Magazine; 5 | 6 | import java.util.Observable; 7 | import java.util.Observer; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: nicholas_smith 12 | * Date: 08/07/13 13 | * Time: 13:26 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class CollectionCustomer extends Customer implements Observer { 17 | 18 | private Observable observable; 19 | 20 | public CollectionCustomer(String name, Observable observable) { 21 | super(name); 22 | this.observable = observable; 23 | observable.addObserver(this); 24 | } 25 | 26 | @Override 27 | public void update(Observable o, Object arg) { 28 | if(o instanceof NewsKiosk){ 29 | Magazine magazine = ((NewsKiosk) o).getLatestMagazine(); 30 | read(magazine); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/koan/resources/Overriding.solution: -------------------------------------------------------------------------------- 1 | reflectOnCallingAMethodOnAClass 2 | [ 3 | expectedBark = "woof-woof"; 4 | ] 5 | 6 | reflectOnTheNoiseABeagleMakes 7 | [ 8 | expectedBark = "yap-yap"; 9 | ] 10 | 11 | reflectOnIteratingOverDogsInACollection 12 | [ 13 | expectedBark = "woof-woof.yap-yap.peanuts."; 14 | ] 15 | 16 | reflectOnHowHidingAStaticMethodIsDifferentToOverridingAnInstanceMethod 17 | [ 18 | dogRollOverResponse = "rolls-over"; 19 | snoopyRollOverResponse = "unlikely"; 20 | mySnoopyRollOverResponse = "rolls-over"; 21 | ] 22 | 23 | reflectOnTheUseOfDefaultMethodsFromInterfaces 24 | [ 25 | expectedResponse = "wag-tail"; 26 | ] 27 | 28 | reflectOnUsingInheritanceToDeterminingOverriding 29 | [ 30 | expectedResponse = "run-about-the-house"; 31 | ] 32 | 33 | reflectOnInheritanceOfDefaultMethodsWithInterfaces 34 | [ 35 | snoopyIsDrawnBy = "hand"; 36 | slhIsDrawnBy = "computer"; 37 | ] 38 | 39 | reflectOnOverridingInterfaceMethodsWithDefaultMethods 40 | [ 41 | snoopysFirstAppearance = "in print"; 42 | slhFirstAppearance = "on tv"; 43 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/varargs/PerformantCompute.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.varargs; 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: nicholas_smith 6 | * Date: 30/05/13 7 | * Time: 10:47 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | public class PerformantCompute { 11 | 12 | public static int max(int arg1) { 13 | return arg1; 14 | } 15 | 16 | public static int max(int arg1, int arg2) { 17 | return arg1 > arg2 ? arg1 : arg2; 18 | } 19 | 20 | public static int max(int arg1, int arg2, int arg3) { 21 | int max = arg1; 22 | 23 | if(arg2 > max){ 24 | max = arg2; 25 | } 26 | if(arg3 > max){ 27 | return arg3; 28 | } 29 | return max; 30 | } 31 | 32 | public static int max(int arg1, int arg2, int arg3, int... remainingArgs) { 33 | int max = max(arg1, arg2, arg3); 34 | for (int arg : remainingArgs) { 35 | if (arg > max) { 36 | max = arg; 37 | } 38 | } 39 | return max; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/generics/GenericsBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.generics; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.runner.RunWith; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | import static org.hamcrest.core.Is.is; 12 | 13 | /** 14 | * Created by nicholas_smith on 24/02/14. 15 | */ 16 | @RunWith(KoanRunner.class) 17 | public class GenericsBasics { 18 | 19 | 20 | /** 21 | * Meditate on why we use generics and what common problem can occur without them. 22 | */ 23 | @Koan 24 | public void reflectOnCompileTimeSafetyOfGenerics(){ 25 | List strings = new ArrayList(); 26 | strings.add("A"); 27 | strings.add(999); 28 | strings.add(new Object()); 29 | 30 | String element = null; 31 | String exceptionMessage = null; 32 | 33 | // (@_@) 34 | 35 | // (^_^) 36 | 37 | assertThat(exceptionMessage, is("java.lang.Integer cannot be cast to java.lang.String")); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/koan/resources/ObserverKoan.solution: -------------------------------------------------------------------------------- 1 | reflectOnTheOutcomeOfACustomerReadingAMagazine 2 | [ 3 | outputUnderMeditation = TOM + " is reading: " + MAGAZINE_CONTENT; 4 | ] 5 | 6 | reflectOnHowObserversAreNotifiedOfUpdatesUsingPush 7 | [ 8 | newsKiosk.newMagazinePublished(magazine); 9 | ] 10 | 11 | reflectOnHowObserversCanBeNotifiedUsingPullRequest 12 | [ 13 | newsKiosk.newMagazinePublishedCollectionOnly(magazine); 14 | ] 15 | 16 | reflectOnNotifyingAllInterestedParties 17 | [ 18 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 19 | HomeDeliveryCustomer harry = new HomeDeliveryCustomer(HARRY, newsKiosk); 20 | 21 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 22 | newsKiosk.newMagazinePublished(magazine); 23 | ] 24 | 25 | reflectOnDifferentTypesOfCustomerWillReceiveDifferentServices 26 | [ 27 | newsKiosk.newMagazinePublishedCollectionOnly(magazine); 28 | ] 29 | 30 | reflectOnNotificationOrder 31 | [ 32 | HomeDeliveryCustomer harry = new HomeDeliveryCustomer(HARRY, newsKiosk); 33 | HomeDeliveryCustomer dick = new HomeDeliveryCustomer(DICK, newsKiosk); 34 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 35 | ] -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/observer/ex2/NewsKiosk.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex2; 2 | 3 | import com.javakoan.lesson.patterns.observer.domain.Magazine; 4 | import com.javakoan.lesson.patterns.observer.domain.Publication; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: nicholas_smith 12 | * Date: 08/07/13 13 | * Time: 14:41 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class NewsKiosk implements Subject { 17 | 18 | private List observers; 19 | 20 | private Magazine latestMagazine; 21 | 22 | public NewsKiosk() { 23 | observers = new ArrayList(); 24 | } 25 | 26 | @Override 27 | public void register(Observer observer) { 28 | observers.add(observer); 29 | } 30 | 31 | @Override 32 | public void remove(Observer observer) { 33 | observers.remove(observer); 34 | } 35 | 36 | @Override 37 | public void notifyObservers(Publication publication) { 38 | for(Observer observer : observers){ 39 | observer.update(publication); 40 | } 41 | } 42 | 43 | public void newMagazinePublished(Magazine magazine){ 44 | this.latestMagazine = magazine; 45 | notifyObservers(magazine); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/java/primitives/SweetShop.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primitives; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: nicholas_smith 8 | * Date: 29/05/13 9 | * Time: 10:09 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | public class SweetShop { 13 | 14 | public int howManySweetsCanIAffordForAFloat(float cash){ 15 | final float TEN_CENTS = 0.10f; 16 | 17 | int itemsBought = 0; 18 | double price = TEN_CENTS; 19 | 20 | for (; cash >= price; price += TEN_CENTS) { 21 | cash -= price; 22 | itemsBought++; 23 | } 24 | 25 | return itemsBought; 26 | } 27 | 28 | public int howManySweetsCanIAffordForABigDecimal(BigDecimal cash){ 29 | final BigDecimal TEN_CENTS = new BigDecimal(".10"); 30 | 31 | int itemsBought = 0; 32 | 33 | for (BigDecimal price = TEN_CENTS; cash.compareTo(price) >= 0; price = price.add(TEN_CENTS)) { 34 | cash = cash.subtract(price); 35 | itemsBought++; 36 | } 37 | 38 | return itemsBought; 39 | } 40 | 41 | public int howManySweetsCanIAffordForAnInt(int cash){ 42 | final int TEN_CENTS = 10; 43 | 44 | int itemsBought = 0; 45 | 46 | for (int price = TEN_CENTS; cash >= price ; price += TEN_CENTS ) { 47 | cash -= price; 48 | itemsBought++; 49 | } 50 | 51 | return itemsBought; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/ex1/NewsKiosk.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import com.javakoan.lesson.patterns.strategy.domain.Magazine; 4 | import com.javakoan.lesson.patterns.strategy.domain.Publication; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: nicholas_smith 12 | * Date: 09/07/13 13 | * Time: 14:45 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | public class NewsKiosk implements Subject{ 17 | 18 | private List observers; 19 | 20 | private Magazine latestMagazine; 21 | 22 | public NewsKiosk() { 23 | observers = new ArrayList(); 24 | } 25 | 26 | @Override 27 | public void register(Observer observer) { 28 | observers.add(observer); 29 | } 30 | 31 | @Override 32 | public void remove(Observer observer) { 33 | observers.remove(observer); 34 | } 35 | 36 | @Override 37 | public void notifyObservers(Publication publication) { 38 | for(Observer observer : observers){ 39 | observer.update(this, publication); 40 | } 41 | } 42 | 43 | public void newMagazinePublished(Magazine magazine){ 44 | 45 | this.latestMagazine = magazine; 46 | notifyObservers(magazine); 47 | } 48 | 49 | public void newMagazinePublishedCollectionOnly(Magazine magazine){ 50 | 51 | this.latestMagazine = magazine; 52 | notifyObservers(null); 53 | } 54 | 55 | public Magazine getLatestMagazine() { 56 | return latestMagazine; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/patterns/decorator/ex2/InputStreamDecoratorKoan.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.ex2; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Enlighten; 5 | import com.javakoan.fixture.annotation.Koan; 6 | import org.junit.runner.RunWith; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | 12 | import static junit.framework.Assert.fail; 13 | import static org.hamcrest.MatcherAssert.assertThat; 14 | import static org.hamcrest.core.Is.is; 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: nicholas_smith 19 | * Date: 08/08/13 20 | * Time: 11:42 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | @RunWith(KoanRunner.class) 24 | public class InputStreamDecoratorKoan { 25 | 26 | /** 27 | * Meditate on the decorator pattern being a useful tool with regards to the mantra of 'open for extension 28 | * closed to modification' 29 | */ 30 | @Koan @Enlighten 31 | public void reflectOnTheDecoratorPatternBeingUsingInTheCoreJavaLibraries(){ 32 | String phrase = "I want to speak leet"; 33 | String phraseUnderMeditation = ""; 34 | 35 | try { 36 | InputStream in = new L33tSp34kInputStream(new ByteArrayInputStream(phrase.getBytes("UTF8"))); 37 | 38 | /* (@_@) */ 39 | 40 | /* (^_^) */ 41 | 42 | in.close(); 43 | } catch (IOException ioe) { 44 | fail("Koan failed with exception: " + ioe.getMessage()); 45 | } 46 | 47 | assertThat(phraseUnderMeditation, is("I w4nt to sp34k l33t")); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/javakoan/lesson/patterns/strategy/domain/Customer.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.domain; 2 | 3 | import com.javakoan.lesson.patterns.strategy.ex1.AcquirementBehavior; 4 | import com.javakoan.lesson.patterns.strategy.ex1.Observer; 5 | import com.javakoan.lesson.patterns.strategy.ex1.Subject; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: nicholas_smith 10 | * Date: 09/07/13 11 | * Time: 14:41 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | public class Customer implements Observer { 15 | 16 | protected String name; 17 | 18 | private AcquirementBehavior behavior; 19 | 20 | private Subject subject; 21 | 22 | public Customer(String name, AcquirementBehavior behavior, Subject subject) { 23 | this.name = name; 24 | this.behavior = behavior; 25 | this.subject = subject; 26 | subject.register(this); 27 | } 28 | 29 | public void read(Publication publication){ 30 | System.out.print(name + " is reading: "); 31 | System.out.println(publication.getContent()); 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | public AcquirementBehavior getBehavior() { 43 | return behavior; 44 | } 45 | 46 | public void setBehavior(AcquirementBehavior behavior) { 47 | this.behavior = behavior; 48 | } 49 | 50 | @Override 51 | public void update(Subject subject, Publication newPublication) { 52 | Publication publication = behavior.acquire(subject, newPublication); 53 | if(publication != null){ // TODO: Why do we need to do this? 54 | read(publication); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Koans 2 | 3 | [![build status](https://github.com/JavaKoan/java-koans/actions/workflows/maven-package.yml/badge.svg?branch=master)](https://github.com/JavaKoan/java-koans/actions) 4 | 5 | This is set of Koans intended to improve fundamental understanding of the Java programming language. 6 | 7 | 8 | What are Koans? 9 | --------------- 10 | 11 | Essentially a Koan is a problem to solve. 12 | 13 | Or from the [Wikipedia](http://en.wikipedia.org/wiki/K%C5%8Dan) article I think the most relevant explanation is: 14 | 15 | > Insight has to be demonstrated. A mere "answer" to a koan is not sufficient. The teacher is not looking for a specific answer, but for evidence that the disciple has grasped the state of mind expressed by the koan itself. 16 | 17 | I think this is particularly relevant to the field of programming. 18 | 19 | For this I am borrowing from the idea of the original [Ruby Koans](http://rubykoans.com/) which involves the practitioner fixing a number of broken unit tests. Using a unit test framework allows for rapid feedback of the topics being discussed and using an IDE we can easily insert breakpoints to explore the code at run-time. 20 | 21 | Prerequisites 22 | ------------- 23 | 24 | Favourite IDE with the ability to execute unit tests. 25 | 26 | Basic understanding of the Java programming language. 27 | 28 | Primers 29 | ------- 30 | 31 | If you are unfamiliar with JUnit you should start with the primer rather than the exercises 32 | 33 | Related Material 34 | ---------------- 35 | 36 | [Learning the Java Language](http://docs.oracle.com/javase/tutorial/java/index.html) 37 | 38 | [Essential Java Classes](http://docs.oracle.com/javase/tutorial/essential/index.html) 39 | 40 | [Collections](http://docs.oracle.com/javase/tutorial/collections/index.html) 41 | 42 | [Effective Java](http://books.google.co.uk/books?isbn=0132778041) 43 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | java-koans 8 | java-koans 9 | 1.0 10 | 11 | 12 | 1.8 13 | 3.1 14 | 1.0.2 15 | 2.1 16 | 17 | 18 | 19 | 20 | ${project.basedir}/src/koan/java 21 | 22 | 23 | ${project.basedir}/src/koan/resources 24 | 25 | 26 | 27 | 28 | 29 | maven-compiler-plugin 30 | ${maven-compiler-plugin.version} 31 | 32 | ${java.version} 33 | ${java.version} 34 | 35 | 36 | 37 | 38 | 39 | 40 | joda-time 41 | joda-time 42 | ${joda-time.version} 43 | 44 | 45 | com.javakoan 46 | koan-annotations 47 | ${koan-annotations.version} 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/koan/resources/PrimitiveBasics.solution: -------------------------------------------------------------------------------- 1 | reflectOnHowToAssignPrimitiveValuesWithLiteralsAndGainInsightIntoDefaultValues 2 | [ 3 | byte8Bit = 100; 4 | short16Bit = 100; 5 | int32Bit = 100; 6 | long64Bit = 100; 7 | float32Bit = 100.00f; 8 | double64bit = 100.00; 9 | char16Bit = 'C'; 10 | bool = true; 11 | ] 12 | 13 | reflectOnMinimumAndMaximumByteValues 14 | [ 15 | maximumByteValue = Byte.MAX_VALUE; 16 | minimumByteValue = Byte.MIN_VALUE; 17 | ] 18 | 19 | reflectOnMinimumAndMaximumIntValues 20 | [ 21 | maximumIntValue = Integer.MAX_VALUE; 22 | minimumIntValue = Integer.MIN_VALUE; 23 | ] 24 | 25 | reflectOnMinimumAndMaximumDoubleValues 26 | [ 27 | maximumDoubleValue = Double.MAX_VALUE; 28 | minimumDoubleValue = Double.MIN_VALUE; 29 | ] 30 | 31 | reflectOnCharactersAlsoHaveMinimumAndMaximumValues 32 | [ 33 | maximumCharValue = '\uffff'; 34 | minimumCharValue = '\u0000'; 35 | ] 36 | 37 | reflectOnEqualityForPrimitives 38 | [ 39 | valueA = 1; 40 | ] 41 | 42 | reflectOnBoxedPrimitivesAreUnBoxedForEvaluationWithPrimitive 43 | [ 44 | valueA = 999; 45 | ] 46 | 47 | reflectOnEqualityForBoxedPrimitives 48 | [ 49 | valueB = 1.00; 50 | ] 51 | 52 | reflectOnIntegersHaveCachedValues 53 | [ 54 | valueB = 1; 55 | ] 56 | 57 | reflectOnDifferentObjectIdsForBoxedPrimitives 58 | [ 59 | valueB = 200; 60 | ] 61 | 62 | reflectOnNullPointersWhenUnboxingIntegers 63 | [ 64 | if(boxedInt > 9){ 65 | fail("Should have thrown null pointer"); 66 | } 67 | ] 68 | 69 | reflectOnSaferToFavouringAPrimitive 70 | [ 71 | if(primitiveInt != 0){ 72 | fail("Uninitialised int should be zero"); 73 | } 74 | ] 75 | 76 | reflectOnUnnecessaryBoxingTakesALongTime 77 | [ 78 | for (long i = 0; i < Integer.MAX_VALUE; i++) { 79 | boxedSum += i; 80 | } 81 | ] -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/generics/TypeErasure.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.generics; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.runner.RunWith; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | import static org.hamcrest.core.Is.is; 12 | 13 | /** 14 | * Created with IntelliJ IDEA. 15 | * User: nicholas_smith 16 | * Date: 12/11/13 17 | * Time: 09:04 18 | * To change this template use File | Settings | File Templates. 19 | */ 20 | @RunWith(KoanRunner.class) 21 | public class TypeErasure { 22 | 23 | /** 24 | * Mediate on how to access elements of a gerericized type 25 | */ 26 | @Koan 27 | public void reflectOnAccessingAGenericizedType(){ 28 | List strings = new ArrayList<>(); 29 | strings.add("A"); 30 | strings.add("B"); 31 | strings.add("C"); 32 | 33 | String firstElement = null; 34 | String secondElement = null; 35 | String thirdElement = null; 36 | 37 | // (@_@) 38 | 39 | // (^_^) 40 | 41 | assertThat(firstElement, is("A")); 42 | assertThat(secondElement, is("B")); 43 | assertThat(thirdElement, is("C")); 44 | } 45 | 46 | /** 47 | * Mediate on how to access elements of a non gerericized type and how it is represented at runtime 48 | */ 49 | @Koan 50 | public void reflectOnWhatTheAboveCodeCompilesAs(){ 51 | List strings = new ArrayList(); 52 | strings.add("A"); 53 | strings.add("B"); 54 | strings.add("C"); 55 | 56 | String firstElement = null; 57 | String secondElement = null; 58 | String thirdElement = null; 59 | 60 | // (@_@) 61 | 62 | // (^_^) 63 | 64 | assertThat(firstElement, is("A")); 65 | assertThat(secondElement, is("B")); 66 | assertThat(thirdElement, is("C")); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/strings/StringConcatenation.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.strings; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.runner.RunWith; 6 | 7 | import static org.junit.Assert.assertTrue; 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: nicholas_smith 12 | * Date: 29/05/13 13 | * Time: 13:08 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @RunWith(KoanRunner.class) 17 | public class StringConcatenation { 18 | 19 | private final String COMPILER = "compiler"; 20 | 21 | /** 22 | * Meditate on the use of StringBuilder and the performance benefits it can bring 23 | * 24 | * Hint: Create a for loop concatenating a string similar to the StringBuilder portion of code. 25 | */ 26 | @Koan 27 | public void meditateOnTheUseOfStringBuilder(){ 28 | 29 | String sampleString = "The " + COMPILER + "automatically handles efficiencies with string concatenation on one line..."; 30 | long startTime, endTime, sbTime, concatenationTime; 31 | 32 | // Start StringBuilder 33 | StringBuilder sbResult = new StringBuilder(""); 34 | 35 | startTime = System.nanoTime(); 36 | for(int i = 0; i < 50; i++){ 37 | sbResult.append(sampleString); 38 | } 39 | endTime = System.nanoTime(); 40 | sbTime = endTime - startTime; 41 | // End StringBuilder 42 | 43 | // Start String Concatenation 44 | String result = ""; 45 | startTime = System.nanoTime(); 46 | // (@_@) 47 | 48 | // (^_^) 49 | endTime = System.nanoTime(); 50 | concatenationTime = endTime - startTime; 51 | // End String Concatenation 52 | 53 | assertTrue(concatenationTime > sbTime); 54 | 55 | System.out.println(String.format("String concatenation (millis): %1$d, StringBuilder (millis): %2$d. " + 56 | "Times greater: %3$d", concatenationTime, sbTime, concatenationTime / sbTime )); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/primer/KoanBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primer; 2 | 3 | 4 | import com.javakoan.fixture.KoanRunner; 5 | import com.javakoan.fixture.annotation.Enlighten; 6 | import com.javakoan.fixture.annotation.Koan; 7 | import com.javakoan.fixture.annotation.Vex; 8 | import org.junit.runner.RunWith; 9 | 10 | import static junit.framework.Assert.fail; 11 | import static org.hamcrest.MatcherAssert.assertThat; 12 | import static org.hamcrest.core.Is.is; 13 | 14 | @RunWith(KoanRunner.class) 15 | public class KoanBasics { 16 | 17 | /** 18 | * Grasshoppers should meditate on how to make the product int equal to 50. 19 | */ 20 | @Koan 21 | public void reflectOnTheProductOfIAndJ(){ 22 | int i = 10; 23 | int j = 5; 24 | int product = 0; 25 | 26 | /* (@_@) Your code starts here */ 27 | product = i * j; 28 | /* (^_^) Your code ends here */ 29 | 30 | assertThat(product, is(50)); 31 | } 32 | 33 | /** 34 | * Grasshoppers should use this koan to understand how to reach enlightenment 35 | */ 36 | @Koan @Enlighten 37 | public void reflectOnProvidingTheSolutionToTheProductOfIAndJ(){ 38 | int i = 10; 39 | int j = 5; 40 | int product = 0; 41 | 42 | /* (@_@) */ 43 | 44 | /* (^_^) */ 45 | 46 | assertThat(product, is(50)); 47 | } 48 | 49 | /** 50 | * Grasshoppers should use this koan to understand how to return to a starting problem 51 | */ 52 | @Koan 53 | public void reflectOnProvidingTheStartingProblemForAKoan(){ 54 | int i = 10; 55 | int j = 5; 56 | 57 | int largest = 0; 58 | 59 | /* (@_@) */ 60 | Math.max(i, j); 61 | /* (^_^) */ 62 | 63 | assertThat(largest, is(i)); 64 | } 65 | 66 | /** 67 | * Grasshoppers should use this koan to understand that koans with start and end markers are ignored. 68 | */ 69 | @Koan 70 | public void reflectOnIgnoringIfKoanDoesNotHaveStartAndEnd(){ 71 | fail("This Koan should be ignored as it has no start (@_@) and end (^_^) markers"); 72 | } 73 | 74 | /** 75 | * Grasshoppers should use this koan to understand that one cannot be Vexed and Enlightened at the same time. 76 | */ 77 | @Koan @Vex @Enlighten 78 | public void reflectOneIgnoringWhenVexedAndEnlightened(){ 79 | /* (@_@) */ 80 | fail("This Koan should be ignored as it is both Vexed and Enlightened"); 81 | /* (^_^) */ 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/patterns/decorator/ex1/PieDecoratorKoan.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.decorator.ex1; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import com.javakoan.lesson.patterns.decorator.domain.ChickenAndLeek; 6 | import com.javakoan.lesson.patterns.decorator.domain.Liquor; 7 | import com.javakoan.lesson.patterns.decorator.domain.Mash; 8 | import com.javakoan.lesson.patterns.decorator.domain.Pie; 9 | import com.javakoan.lesson.patterns.decorator.domain.SteakAndKidney; 10 | import org.junit.runner.RunWith; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.core.Is.is; 14 | 15 | @RunWith(KoanRunner.class) 16 | public class PieDecoratorKoan { 17 | 18 | 19 | /** 20 | * Meditate on how to decorate a Pie object with mash. 21 | */ 22 | @Koan 23 | public void reflectOnThePriceOfAChickenAndLeekPieWithMash(){ 24 | Pie pie = new ChickenAndLeek(); 25 | 26 | /* (@_@) */ 27 | 28 | /* (^_^) */ 29 | 30 | assertThat(pie.cost(), is(350)); 31 | } 32 | 33 | 34 | /** 35 | * Meditate on the what the description of the pie will be after adding liquor 36 | */ 37 | @Koan 38 | public void reflectOnTheDescriptionOfAChickenAndLeekPieWithMash(){ 39 | Pie pie = new ChickenAndLeek(); 40 | pie = new Liquor(pie); 41 | 42 | String descriptionUnderMeditation = ""; 43 | 44 | /* (@_@) */ 45 | 46 | /* (^_^) */ 47 | 48 | assertThat(descriptionUnderMeditation, is(pie.getDescription())); 49 | } 50 | 51 | /** 52 | * Mediate on how to construct a steak and kidney pie with double mash and liquor 53 | */ 54 | @Koan 55 | public void reflectOneUsingAddingTheSameDecorationTwice(){ 56 | Pie pie = new SteakAndKidney(); 57 | 58 | /* (@_@) */ 59 | 60 | /* (^_^) */ 61 | 62 | assertThat(pie.cost(), is(550)); 63 | } 64 | 65 | /** 66 | * Meditate on the implementation of your decorator can produce different results depending on the decoration order. 67 | */ 68 | @Koan 69 | public void reflectOnTheDecorationOrderBeingImportant(){ 70 | Pie pie = new SteakAndKidney(); 71 | pie = new Mash(pie); 72 | pie = new Liquor(pie); 73 | pie = new Mash(pie); 74 | 75 | String descriptionUnderMeditation = ""; 76 | 77 | /* (@_@) */ 78 | 79 | /* (^_^) */ 80 | 81 | assertThat(descriptionUnderMeditation, is(pie.getDescription())); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/overloading/OverloadingBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overloading; 2 | 3 | import com.javakoan.lesson.java.overriding.Beagle; 4 | import com.javakoan.lesson.java.overriding.Dog; 5 | import com.javakoan.lesson.java.overriding.Snoopy; 6 | import com.javakoan.fixture.KoanRunner; 7 | import com.javakoan.fixture.annotation.Koan; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import static org.hamcrest.MatcherAssert.assertThat; 14 | import static org.hamcrest.core.Is.is; 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: nicholas_smith 19 | * Date: 29/05/13 20 | * Time: 16:37 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | @RunWith(KoanRunner.class) 24 | public class OverloadingBasics { 25 | 26 | /** 27 | * Meditate on the fact that unlike overriding which occurs at run-time, overloading is assessed at compile time 28 | */ 29 | @Koan 30 | public void meditateThatOverloadingIsAssessedAtCompileTime(){ 31 | Dog[] dogs = { new Dog(), new Beagle(), Snoopy.getInstance() }; 32 | 33 | List typeResponses = new ArrayList<>(); 34 | 35 | for (Dog d : dogs) { 36 | typeResponses.add(DogClassifier.classify(d)); 37 | } 38 | 39 | String firstDogClassification = "It's just some mongrel"; 40 | String secondDogClassification = "It's a Beagle"; 41 | String thirdDogClassification = "It's Snoopy!"; 42 | 43 | /* (@_@) */ 44 | 45 | /* (^_^) */ 46 | 47 | assertThat(typeResponses.get(0), is(firstDogClassification)); 48 | assertThat(typeResponses.get(1), is(secondDogClassification)); 49 | assertThat(typeResponses.get(2), is(thirdDogClassification)); 50 | } 51 | 52 | /** 53 | * Meditate on using alternative approaches when functionality can be easily misinterpreted. 54 | */ 55 | @Koan 56 | public void meditateOnHowToResolveOverloadingConfusion(){ 57 | Dog[] dogs = { new Dog(), new Beagle(), Snoopy.getInstance() }; 58 | 59 | List typeResponses = new ArrayList<>(); 60 | 61 | for (Dog d : dogs) { 62 | typeResponses.add(DogClassifier.classifyAnyDog(d)); 63 | } 64 | 65 | String firstDogClassification = "It's just some mongrel"; 66 | String secondDogClassification = "It's just some mongrel"; 67 | String thirdDogClassification = "It's just some mongrel"; 68 | 69 | /* (@_@) */ 70 | 71 | /* (^_^) */ 72 | 73 | assertThat(typeResponses.get(0), is(firstDogClassification)); 74 | assertThat(typeResponses.get(1), is(secondDogClassification)); 75 | assertThat(typeResponses.get(2), is(thirdDogClassification)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/patterns/observer/ex2/NewsKioskTest.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex2; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import com.javakoan.lesson.patterns.observer.domain.Customer; 7 | import com.javakoan.lesson.patterns.observer.domain.Magazine; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.PrintStream; 11 | 12 | import static junit.framework.Assert.assertEquals; 13 | 14 | /** 15 | * Created with IntelliJ IDEA. 16 | * User: nicholas_smith 17 | * Date: 08/07/13 18 | * Time: 14:58 19 | * To change this template use File | Settings | File Templates. 20 | */ 21 | public class NewsKioskTest { 22 | 23 | private final ByteArrayOutputStream systemOutContent = new ByteArrayOutputStream(); 24 | 25 | private NewsKiosk newsKiosk; 26 | 27 | private final int MAGAZINE_EDITION = 2; 28 | private final String MAGAZINE_CONTENT = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; 29 | 30 | private final String TOM = "Tom"; 31 | 32 | @Before 33 | public void setUp(){ 34 | newsKiosk = new NewsKiosk(); 35 | } 36 | 37 | @Test 38 | public void shouldReadMagazine(){ 39 | Customer tom = new Customer(TOM); 40 | 41 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 42 | 43 | tom.read(magazine); 44 | 45 | String outputUnderTest = systemOutContent.toString().trim(); 46 | 47 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT, outputUnderTest); 48 | } 49 | 50 | @Test 51 | public void shouldPushMagazineToCustomer(){ 52 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 53 | 54 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 55 | newsKiosk.newMagazinePublished(magazine); 56 | 57 | String outputUnderTest = systemOutContent.toString().trim(); 58 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT, outputUnderTest); 59 | } 60 | 61 | @Test 62 | public void shouldNotPushMagazineToCustomerAfterRemoval(){ 63 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 64 | 65 | Magazine magazine1 = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 66 | newsKiosk.newMagazinePublished(magazine1); 67 | 68 | String outputUnderTest = systemOutContent.toString().trim(); 69 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT, outputUnderTest); 70 | 71 | newsKiosk.remove(tom); 72 | systemOutContent.reset(); 73 | 74 | Magazine magazine2 = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 75 | newsKiosk.newMagazinePublished(magazine2); 76 | 77 | outputUnderTest = systemOutContent.toString().trim(); 78 | assertEquals("", outputUnderTest); 79 | } 80 | 81 | @Test 82 | public void shouldAllowPullForCustomer(){ 83 | // TODO: Implement code of a collection/pull customer 84 | } 85 | 86 | @Test 87 | public void shouldPushOneFinalMagazineAfterRemoval(){ 88 | // TODO: Customers pay in advance so will get one last paper, before being un subscribed 89 | } 90 | 91 | @Before 92 | public void setUpSystemStreams(){ 93 | System.setOut(new PrintStream(systemOutContent)); 94 | } 95 | 96 | @After 97 | public void cleanUpSystemStreams() { 98 | System.setOut(null); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/invariants/InvariantBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.invariants; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import com.javakoan.lesson.java.mutables.BrokenPeriod; 6 | import com.javakoan.lesson.java.mutables.Period; 7 | import org.junit.runner.RunWith; 8 | 9 | import java.util.Date; 10 | 11 | import static junit.framework.Assert.assertTrue; 12 | import static junit.framework.Assert.fail; 13 | import static org.hamcrest.CoreMatchers.sameInstance; 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.core.Is.is; 16 | import static org.hamcrest.core.IsNot.not; 17 | 18 | @RunWith(KoanRunner.class) 19 | public class InvariantBasics { 20 | 21 | private static final Date APRIL_1_2013 = new Date(1364770800000L); 22 | private static final Date MAY_29_2013 = new Date(1369782000000L); 23 | private static final Date JUNE_15_2013 = new Date(1371250800000L); 24 | 25 | /** 26 | * Meditate on the use of constructors to help enforce class invariants 27 | */ 28 | @Koan 29 | public void reflectOnUsingConstructorsToPreserveInvariants(){ 30 | 31 | Date start = new Date(JUNE_15_2013.getTime()); 32 | Date end = new Date(MAY_29_2013.getTime()); 33 | 34 | String messageUnderMeditation = ""; 35 | 36 | /* (@_@) */ 37 | 38 | /* (^_^) */ 39 | 40 | try { 41 | BrokenPeriod brokenPeriod = new BrokenPeriod(start, end); 42 | fail("Should have thrown Exception"); 43 | } catch (Exception e){ 44 | assertThat(messageUnderMeditation, is(e.getMessage())); 45 | } 46 | } 47 | 48 | /** 49 | * Meditate on how invariants can still be compromised by retaining original reference. 50 | */ 51 | @Koan 52 | public void reflectOnCompromisingThePeriodByMakingEndBeforeStart(){ 53 | 54 | Date start = new Date(MAY_29_2013.getTime()); 55 | Date end = new Date(JUNE_15_2013.getTime()); 56 | 57 | BrokenPeriod brokenPeriod = new BrokenPeriod(start, end); 58 | 59 | assertTrue(brokenPeriod.getStart().before(brokenPeriod.getEnd())); 60 | 61 | /* (@_@) */ 62 | 63 | /* (^_^) */ 64 | 65 | assertTrue(brokenPeriod.getEnd().before(brokenPeriod.getStart())); 66 | } 67 | 68 | /** 69 | * Meditate on how invariants can be compromised by exploiting accessor methods. 70 | */ 71 | @Koan 72 | public void reflectAgainOnCompromisingThePeriodByMakingEndBeforeStart(){ 73 | 74 | Date start = new Date(MAY_29_2013.getTime()); 75 | Date end = new Date(JUNE_15_2013.getTime()); 76 | 77 | BrokenPeriod brokenPeriod = new BrokenPeriod(start, end); 78 | 79 | assertTrue(brokenPeriod.getStart().before(brokenPeriod.getEnd())); 80 | 81 | /* (@_@) */ 82 | 83 | /* (^_^) */ 84 | 85 | assertTrue(brokenPeriod.getEnd().before(brokenPeriod.getStart())); 86 | } 87 | 88 | 89 | /** 90 | * Meditate on using defensive copies to preserve class invariants. 91 | */ 92 | @Koan 93 | public void reflectOnUsingDefensiveCopiesToPreserveClassInvariants(){ 94 | 95 | Date start = new Date(MAY_29_2013.getTime()); 96 | Date end = new Date(JUNE_15_2013.getTime()); 97 | 98 | Period period = new Period(start, end); 99 | 100 | Date startUnderMeditation = start; 101 | Date endUnderMeditation = end; 102 | 103 | /* (@_@) */ 104 | 105 | /* (^_^) */ 106 | 107 | assertThat(start, not(sameInstance(startUnderMeditation))); 108 | assertThat(end, not(sameInstance(endUnderMeditation))); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/primitives/Autoboxing.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primitives; 2 | 3 | 4 | import com.javakoan.fixture.KoanRunner; 5 | import com.javakoan.fixture.annotation.Koan; 6 | import org.junit.runner.RunWith; 7 | 8 | import java.util.*; 9 | 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | import static org.hamcrest.core.Is.is; 12 | import static org.hamcrest.core.IsNot.not; 13 | import static org.hamcrest.core.IsSame.sameInstance; 14 | 15 | /** 16 | * This set of Koans aims to improve the students understanding of Autoboxing and Unboxing 17 | * 18 | * Recommend reading: http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html 19 | */ 20 | @RunWith(KoanRunner.class) 21 | public class Autoboxing { 22 | 23 | /** 24 | * Meditate on the meaning of Autoboxing when assigning variables 25 | */ 26 | @Koan 27 | public void reflectedOnAutoboxingWithAssignment(){ 28 | Integer i = new Integer(0); 29 | 30 | /* (@_@) */ 31 | 32 | /* (^_^) */ 33 | 34 | assertThat(i, is(5)); 35 | } 36 | 37 | /** 38 | * Meditate on the meaning of Autoboxing when calling methods 39 | */ 40 | @Koan 41 | public void reflectedOnAutoboxingWhenPassingParametersWhileCallingSquareOf(){ 42 | int i = Boxer.squareOf(new Integer(10)); 43 | 44 | /* (@_@) */ 45 | 46 | /* (^_^) */ 47 | 48 | assertThat(i, is(25)); 49 | } 50 | 51 | 52 | /** 53 | * Meditate on the meaning of Unboxing when assigning values 54 | */ 55 | @Koan 56 | public void reflectedOnUnboxingWithAssignment(){ 57 | int i = 0; 58 | 59 | /* (@_@) */ 60 | 61 | /* (^_^) */ 62 | 63 | assertThat(i, is(5)); 64 | } 65 | 66 | /** 67 | * Meditate on the a more likely occurrence of unboxing when assigning values 68 | */ 69 | @Koan 70 | public void reflectedOnUnboxingWithAssignmentInALoop(){ 71 | List integers = Arrays.asList(1, 2, 3); 72 | 73 | int sum = 0; 74 | 75 | for (Integer i : integers){ 76 | /* (@_@) */ 77 | 78 | /* (^_^) */ 79 | } 80 | 81 | assertThat(sum, is(6)); 82 | } 83 | 84 | /** 85 | * Meditate on the meaning of unboxing when calling methods 86 | */ 87 | @Koan 88 | public void reflectedOnUnboxingWhenPassingParametersWhileCallingDoubleOf(){ 89 | int i = Boxer.doubleOf(10); 90 | 91 | /* (@_@) */ 92 | 93 | /* (^_^) */ 94 | 95 | assertThat(i, is(10)); 96 | } 97 | 98 | 99 | /** 100 | * Meditate on the difference between removing elements from a Set and List. 101 | */ 102 | @Koan 103 | public void reflectOnTheDifferencesBetweenCollectionImplementationsWhenRemovingItems(){ 104 | 105 | Set set = new TreeSet<>(); 106 | List list = new ArrayList<>(); 107 | 108 | for (int i = -3; i < 3; i++) { 109 | set.add(i); 110 | list.add(i); 111 | } 112 | 113 | for (int i = 0; i < 3; i++) { 114 | set.remove(i); 115 | list.remove(i); 116 | } 117 | 118 | String expectedSetOutput = ""; 119 | String expectedListOutput = ""; 120 | 121 | /* (@_@) */ 122 | 123 | /* (^_^) */ 124 | 125 | assertThat(set.toString(), is(expectedSetOutput)); 126 | assertThat(list.toString(), is(expectedListOutput)); 127 | } 128 | 129 | @Koan 130 | public void reflectOnHowBoxingOccursWithValueOfNotNew(){ 131 | Long l1 = 1L; 132 | Long l2 = Long.valueOf(1L); 133 | Long l3 = 1L; 134 | 135 | Long l5 = 128L; 136 | Long l6 = Long.valueOf(128L); 137 | 138 | /* (@_@) */ 139 | 140 | /* (^_^) */ 141 | 142 | assertThat(l1, is(sameInstance(l2))); 143 | assertThat(l1, not(sameInstance(l3))); 144 | 145 | assertThat(l5, is(sameInstance(l6))); 146 | } 147 | 148 | private static class Boxer { 149 | 150 | public static int squareOf(Integer number){ 151 | return number * number; 152 | } 153 | 154 | public static int doubleOf(int number){ 155 | return number + number; 156 | } 157 | } 158 | 159 | } 160 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/patterns/strategy/ex1/NewsKioskTest.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.strategy.ex1; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import com.javakoan.lesson.patterns.strategy.domain.Customer; 7 | import com.javakoan.lesson.patterns.strategy.domain.Magazine; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.PrintStream; 11 | 12 | import static junit.framework.Assert.assertEquals; 13 | import static junit.framework.Assert.assertFalse; 14 | import static junit.framework.Assert.assertTrue; 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: nicholas_smith 19 | * Date: 10/07/13 20 | * Time: 14:26 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | public class NewsKioskTest { 24 | 25 | private final ByteArrayOutputStream systemOutContent = new ByteArrayOutputStream(); 26 | 27 | private NewsKiosk newsKiosk; 28 | 29 | private final int MAGAZINE_EDITION = 2; 30 | private final String MAGAZINE_CONTENT_A = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; 31 | private final String MAGAZINE_CONTENT_B = "But I must explain to you how all this mistaken idea of"; 32 | 33 | private final String TOM = "Tom"; 34 | private final String HARRY = "Harry"; 35 | 36 | 37 | @Before 38 | public void setUp(){ 39 | newsKiosk = new NewsKiosk(); 40 | } 41 | 42 | 43 | @Test 44 | public void shouldPushMagazineToCustomer(){ 45 | Customer tom = new Customer(TOM, new AcquirementByDelivery(), newsKiosk); 46 | 47 | Magazine magazine = new Magazine(MAGAZINE_CONTENT_A, MAGAZINE_EDITION); 48 | newsKiosk.newMagazinePublished(magazine); 49 | 50 | String outputUnderTest = systemOutContent.toString().trim(); 51 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT_A, outputUnderTest); 52 | } 53 | 54 | @Test 55 | public void shouldAllowPullForCustomer(){ 56 | Customer tom = new Customer(TOM, new AcquirementByCollection(), newsKiosk); 57 | 58 | Magazine magazine = new Magazine(MAGAZINE_CONTENT_A, MAGAZINE_EDITION); 59 | newsKiosk.newMagazinePublishedCollectionOnly(magazine); 60 | 61 | String outputUnderTest = systemOutContent.toString().trim(); 62 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT_A, outputUnderTest); 63 | 64 | outputUnderTest = systemOutContent.toString().trim(); 65 | assertEquals(TOM + " is reading: " + MAGAZINE_CONTENT_A, outputUnderTest); 66 | } 67 | 68 | @Test 69 | public void shouldNotPushToDeliveryCustomer(){ 70 | Customer tom = new Customer(TOM, new AcquirementByDelivery(), newsKiosk); 71 | Customer harry = new Customer(HARRY, new AcquirementByCollection(), newsKiosk); 72 | 73 | Magazine magazine = new Magazine(MAGAZINE_CONTENT_A, MAGAZINE_EDITION); 74 | newsKiosk.newMagazinePublishedCollectionOnly(magazine); 75 | 76 | String tomIsReading = TOM + " is reading: " + MAGAZINE_CONTENT_A; 77 | String harryIsReading = HARRY + " is reading: " + MAGAZINE_CONTENT_A; 78 | 79 | String outputUnderTest = systemOutContent.toString().trim(); 80 | 81 | assertTrue(outputUnderTest.contains(harryIsReading)); 82 | assertFalse(outputUnderTest.contains(tomIsReading)); 83 | } 84 | 85 | @Test 86 | public void shouldBeAbleToChangeCustomerAcquisitionBehaviour(){ 87 | Customer tom = new Customer(TOM, new AcquirementByDelivery(), newsKiosk); 88 | 89 | Magazine magazineA = new Magazine(MAGAZINE_CONTENT_A, MAGAZINE_EDITION); 90 | newsKiosk.newMagazinePublishedCollectionOnly(magazineA); 91 | 92 | tom.setBehavior(new AcquirementByCollection()); 93 | 94 | Magazine magazineB = new Magazine(MAGAZINE_CONTENT_B, MAGAZINE_EDITION); 95 | newsKiosk.newMagazinePublishedCollectionOnly(magazineB); 96 | 97 | String tomIsReadingA = TOM + " is reading: " + MAGAZINE_CONTENT_A; 98 | String tomIsReadingB = TOM + " is reading: " + MAGAZINE_CONTENT_B; 99 | String outputUnderTest = systemOutContent.toString().trim(); 100 | 101 | assertFalse(outputUnderTest.contains(tomIsReadingA)); 102 | assertTrue(outputUnderTest.contains(tomIsReadingB)); 103 | } 104 | 105 | @Before 106 | public void setUpSystemStreams(){ 107 | System.setOut(new PrintStream(systemOutContent)); 108 | } 109 | 110 | @After 111 | public void cleanUpSystemStreams() { 112 | System.setOut(null); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/primitives/FunWithFloats.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primitives; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.Before; 6 | import org.junit.runner.RunWith; 7 | 8 | import java.math.BigDecimal; 9 | 10 | import static junit.framework.Assert.fail; 11 | import static org.hamcrest.MatcherAssert.assertThat; 12 | import static org.hamcrest.core.Is.is; 13 | 14 | @RunWith(KoanRunner.class) 15 | public class FunWithFloats { 16 | 17 | private SweetShop sweetShop; 18 | 19 | /** 20 | * Meditate on the String value of a floating point number. Consider the value of the expressed floating 21 | * point number f, perform a calculation to reach the desired value and convert the float to a String representation 22 | */ 23 | @Koan 24 | public void reflectOnProducingTheStringValue100000() { 25 | float f = 1.0e+4f; 26 | 27 | String stringValue = ""; 28 | 29 | /* (@_@) */ 30 | /* (^_^) */ 31 | 32 | assertThat(stringValue, is("100000.0")); 33 | } 34 | 35 | /** 36 | * Meditate on the String value of a floating point number that exceeds the maximum level of precision. 37 | * Consider the value of the double d, make d larger than exceed the maximum precision and examine the String representation 38 | */ 39 | @Koan 40 | public void reflectOnProducingTheStringValueInfinity() { 41 | double d = 1.0e+308; 42 | 43 | String stringValue = ""; 44 | 45 | /* (@_@) */ 46 | /* (^_^) */ 47 | 48 | assertThat(stringValue, is("Infinity")); 49 | } 50 | 51 | /** 52 | * Meditate on producing a String value on Not a Number "NaN" given the double value d; 53 | */ 54 | @Koan 55 | public void reflectOnProducingTheStringValueNaN() { 56 | double d = 0.0; 57 | 58 | String stringValue = ""; 59 | 60 | /* (@_@) */ 61 | /* (^_^) */ 62 | 63 | assertThat(stringValue, is("NaN")); 64 | } 65 | 66 | /** 67 | * Meditate on how to produce an ArithmeticException and receive the exception message expected. 68 | */ 69 | @Koan 70 | public void reflectOnHowAnArithmeticExceptionCanOccur() { 71 | int toCauseException = 100; 72 | try { 73 | 74 | /* (@_@) */ 75 | /* (^_^) */ 76 | 77 | fail(); 78 | } catch (ArithmeticException ae) { 79 | assertThat(ae.getMessage(), is("/ by zero")); 80 | } 81 | } 82 | 83 | 84 | /** 85 | * Meditate on how many sweets you can buy in the shop with 1 dollar represented as a float given the following 86 | * 87 | * 1st Item = 0.10 5th Item = 0.50 88 | * 2nd Item = 0.20 6th Item = 0.60 89 | * 3rd Item = 0.30 7th Item = 0.70 90 | * 4th Item = 0.40 8th Item = 0.80 91 | * =========================================== 92 | * Total 4 Items = 0.10 + 0.20 + 0.30 + 0.40 = $1.00 93 | */ 94 | @Koan 95 | public void reflectOnFloatBeingAPoorChoiceToRepresentCurrency() { 96 | 97 | float cash = 1.00f; 98 | 99 | int expectedNumberOfSweets = 0; 100 | 101 | /* (@_@) */ 102 | /* (^_^) */ 103 | 104 | int actualNumberOfSweets = sweetShop.howManySweetsCanIAffordForAFloat(cash); 105 | 106 | assertThat(expectedNumberOfSweets, is(actualNumberOfSweets)); 107 | } 108 | 109 | /** 110 | * Mediate on how many sweets you can buy at the same shop when using BigDecimal to represent currency 111 | */ 112 | @Koan 113 | public void reflectOnBigDecimalBeingABetterChoiceForCurrency() { 114 | 115 | BigDecimal cash = new BigDecimal("1.00"); 116 | 117 | int expectedNumberOfSweets = 0; 118 | 119 | /* (@_@) */ 120 | /* (^_^) */ 121 | 122 | int noItems = sweetShop.howManySweetsCanIAffordForABigDecimal(cash); 123 | 124 | assertThat(expectedNumberOfSweets, is(noItems)); 125 | } 126 | 127 | /** 128 | * Mediate on how many sweets you can buy at the same shop when using an int to represent currency 129 | */ 130 | @Koan 131 | public void reflectOnIntBeingABetterAndMoreEfficientChoiceForCurrency() throws Exception { 132 | 133 | int cash = 100; 134 | 135 | int expectedNumberOfSweets = 0; 136 | 137 | /* (@_@) */ 138 | /* (^_^) */ 139 | 140 | int noItems = sweetShop.howManySweetsCanIAffordForAnInt(cash); 141 | 142 | assertThat(expectedNumberOfSweets, is(noItems)); 143 | } 144 | 145 | 146 | @Before 147 | public void setUp() { 148 | sweetShop = new SweetShop(); 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/primer/JUnitBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primer; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static junit.framework.Assert.assertSame; 13 | import static org.hamcrest.CoreMatchers.equalTo; 14 | import static org.hamcrest.CoreMatchers.instanceOf; 15 | import static org.hamcrest.CoreMatchers.is; 16 | import static org.hamcrest.CoreMatchers.not; 17 | import static org.hamcrest.CoreMatchers.nullValue; 18 | import static org.hamcrest.CoreMatchers.sameInstance; 19 | import static org.hamcrest.MatcherAssert.assertThat; 20 | import static org.junit.Assert.assertEquals; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertNotSame; 23 | import static org.junit.Assert.assertNull; 24 | import static org.junit.Assert.assertTrue; 25 | import static org.junit.Assert.fail; 26 | 27 | /** 28 | * Primer for JUnit. Basics required for the exercises 29 | *

30 | * Right clicking on a method and selecting "Run..." will allow you to execute an individual test in most IDEs 31 | *

32 | * Right clicking at the class level and selecting "Run..." will allows you to run all tests 33 | */ 34 | public class JUnitBasics { 35 | 36 | /* 37 | Test two objects are logically equal 38 | */ 39 | @Test 40 | public void shouldDemonstrateAssertEquals() { 41 | 42 | BigDecimal a = new BigDecimal(1.0); 43 | BigDecimal b = new BigDecimal(1.0); 44 | 45 | assertEquals(a, b); // Pure Junit 46 | assertThat(a, equalTo(b)); // Hamcrest equivalent 47 | assertThat(a, is(b)); // Hamcrest equivalent 48 | } 49 | 50 | /* 51 | Test two objects have reference the same object 52 | */ 53 | @Test 54 | public void shouldDemonstrateAssertSame() { 55 | Integer a = new Integer(1); 56 | Integer b = a; 57 | 58 | assertSame(a, b); // JUnit 59 | assertThat(a, sameInstance(b)); // Hamcrest equivalent 60 | } 61 | 62 | /* 63 | Test two objects do not reference the same object 64 | */ 65 | @Test 66 | public void shouldDemonstrateAssertNotSame() { 67 | Integer a = new Integer(1); 68 | Integer b = new Integer(1); 69 | 70 | assertNotSame(a, b); 71 | assertThat(a, not(sameInstance(b))); 72 | } 73 | 74 | /* 75 | Test condition is true 76 | */ 77 | @Test 78 | public void shouldDemonstrateAssertTrue() { 79 | boolean a = true; 80 | 81 | assertTrue(a); 82 | assertThat(a, is(true)); 83 | } 84 | 85 | /* 86 | Test condition is false 87 | */ 88 | @Test 89 | public void shouldDemonstrateAssertFalse() { 90 | boolean b = false; 91 | 92 | assertFalse(b); 93 | assertThat(b, not(true)); 94 | } 95 | 96 | /* 97 | Test object is null 98 | */ 99 | @Test 100 | public void shouldDemonstrateAssertNull() { 101 | Object iAmNull = null; 102 | 103 | assertNull(iAmNull); 104 | assertThat(iAmNull, nullValue()); 105 | } 106 | 107 | /* 108 | Force test to fail 109 | */ 110 | @Test @Ignore 111 | public void wontPass() { 112 | fail(); 113 | } 114 | 115 | /* 116 | Exception handling test paradigm 117 | */ 118 | @Test 119 | public void shouldDemonstrateHandlingExceptionsWithTests() { 120 | 121 | Integer iAmNull = null; 122 | 123 | try { 124 | iAmNull.intValue(); 125 | fail(); 126 | } catch (NullPointerException npe) { 127 | assertTrue(npe instanceof NullPointerException); 128 | assertThat(npe, instanceOf(NullPointerException.class)); 129 | // In a real test assert something more meaningful here like expected message 130 | } 131 | } 132 | 133 | private List messages = new ArrayList(); 134 | 135 | /* 136 | Demonstrate setUp @Before is called before all tests 137 | */ 138 | @Test 139 | public void shouldDemonstrateExecutionOrder() { 140 | messages.add("Added During Test"); 141 | 142 | assertEquals(messages.get(0), "Added Before Test"); 143 | assertEquals(messages.get(1), "Added During Test"); 144 | } 145 | 146 | @Before 147 | public void setUp() { 148 | messages.add("Added Before Test"); 149 | } 150 | 151 | @After 152 | public void tearDown() { 153 | // Uncomment to execute after every test 154 | // System.out.println("Test Complete"); 155 | } 156 | 157 | // Allows use of debugger. 158 | // setUp & tearDown historic names before annotation based Before & After 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/overriding/Overriding.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.overriding; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Enlighten; 5 | import com.javakoan.fixture.annotation.Koan; 6 | import com.javakoan.fixture.annotation.Vex; 7 | import org.junit.runner.RunWith; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.core.Is.is; 14 | 15 | /** 16 | * This set of Koans aims to improve the students understanding of overriding 17 | * 18 | * Recommended reading: http://docs.oracle.com/javase/tutorial/java/IandI/override.html 19 | * http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html 20 | */ 21 | @RunWith(KoanRunner.class) 22 | public class Overriding { 23 | 24 | /** 25 | * Meditate on the noise a dog makes 26 | */ 27 | @Koan 28 | public void reflectOnCallingAMethodOnAClass() { 29 | 30 | Dog aDog = new Dog(); 31 | 32 | String expectedBark = ""; 33 | 34 | /* (@_@) */ 35 | 36 | /* (^_^) */ 37 | 38 | assertThat(aDog.bark(), is(expectedBark)); 39 | } 40 | 41 | /** 42 | * Meditate on the noise a beagle makes, and how overriding is assessed at run time. 43 | */ 44 | @Koan 45 | public void reflectOnTheNoiseABeagleMakes() { 46 | 47 | Dog aBeagle = new Beagle(); 48 | 49 | String expectedBark = ""; 50 | 51 | /* (@_@) */ 52 | 53 | /* (^_^) */ 54 | 55 | assertThat(aBeagle.bark(), is(expectedBark)); 56 | } 57 | 58 | /** 59 | * Meditate on the usefulness of overriding in collections 60 | */ 61 | @Koan 62 | public void reflectOnIteratingOverDogsInACollection() { 63 | 64 | List dogs = Arrays.asList(new Dog(), new Beagle(), Snoopy.getInstance()); 65 | 66 | String barkUnderMeditation = ""; 67 | for (Dog dog : dogs) { 68 | barkUnderMeditation += dog.bark(); 69 | barkUnderMeditation += "."; 70 | } 71 | 72 | String expectedBark = ""; 73 | 74 | /* (@_@) */ 75 | 76 | /* (^_^) */ 77 | 78 | assertThat(barkUnderMeditation, is(expectedBark)); 79 | } 80 | 81 | 82 | /** 83 | * Meditate on the a hiding static methods with the rollOver command. 84 | */ 85 | @Koan 86 | public void reflectOnHowHidingAStaticMethodIsDifferentToOverridingAnInstanceMethod() { 87 | 88 | Dog mySnoopy = Snoopy.getInstance(); 89 | 90 | String dogRollOverResponse = ""; 91 | String snoopyRollOverResponse = ""; 92 | String mySnoopyRollOverResponse = ""; 93 | 94 | /* (@_@) */ 95 | 96 | /* (^_^) */ 97 | 98 | assertThat(Dog.rollOver(), is(dogRollOverResponse)); 99 | assertThat(Snoopy.rollOver(), is(snoopyRollOverResponse)); 100 | assertThat(mySnoopy.rollOver(), is(mySnoopyRollOverResponse)); 101 | } 102 | 103 | 104 | /** 105 | * Meditate on the walkies interface and its default method implementation 106 | */ 107 | @Koan 108 | public void reflectOnTheUseOfDefaultMethodsFromInterfaces() { 109 | 110 | Dog aBeagle = new Beagle(); 111 | 112 | String expectedResponse = ""; 113 | 114 | /* (@_@) */ 115 | 116 | /* (^_^) */ 117 | 118 | assertThat(aBeagle.walkies(), is(expectedResponse)); 119 | } 120 | 121 | /** 122 | * Meditate on the walkies interface and how default methods are overridden 123 | */ 124 | @Koan 125 | public void reflectOnUsingInheritanceToDeterminingOverriding() { 126 | 127 | Dog slh = new SantasLittleHelper(); 128 | 129 | String expectedResponse = ""; 130 | 131 | /* (@_@) */ 132 | 133 | /* (^_^) */ 134 | 135 | assertThat(slh.walkies(), is(expectedResponse)); 136 | } 137 | 138 | /** 139 | * Meditate on the cartoon and modern cartoon interface hierarchy 140 | */ 141 | @Koan 142 | public void reflectOnInheritanceOfDefaultMethodsWithInterfaces() { 143 | 144 | Cartoon snoopy = Snoopy.getInstance(); 145 | Cartoon slh = new SantasLittleHelper(); 146 | 147 | String snoopyIsDrawnBy = ""; 148 | String slhIsDrawnBy = ""; 149 | 150 | /* (@_@) */ 151 | 152 | /* (^_^) */ 153 | 154 | assertThat(snoopy.drawnBy(), is(snoopyIsDrawnBy)); 155 | assertThat(slh.drawnBy(), is(slhIsDrawnBy)); 156 | } 157 | 158 | 159 | /** 160 | * Meditate on the overriding interface methods with default methods in the interface hierarchy 161 | */ 162 | @Koan 163 | public void reflectOnOverridingInterfaceMethodsWithDefaultMethods() { 164 | 165 | Cartoon snoopy = Snoopy.getInstance(); 166 | Cartoon slh = new SantasLittleHelper(); 167 | 168 | String snoopysFirstAppearance = ""; 169 | String slhFirstAppearance = ""; 170 | 171 | /* (@_@) */ 172 | 173 | /* (^_^) */ 174 | 175 | assertThat(snoopy.firstAppearance(), is(snoopysFirstAppearance)); 176 | assertThat(slh.firstAppearance(), is(slhFirstAppearance)); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/varargs/VarargsBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.varargs; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.runner.RunWith; 6 | 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.core.Is.is; 9 | import static org.hamcrest.core.IsInstanceOf.instanceOf; 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: nicholas_smith 14 | * Date: 30/05/13 15 | * Time: 09:53 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | @RunWith(KoanRunner.class) 19 | public class VarargsBasics { 20 | 21 | /** 22 | * Meditate on calling a method that has been defined with a variable number of arguments of the same type. 23 | */ 24 | @Koan 25 | public void reflectOnCallingAMethodThatHasAVariableNumberOfArguments(){ 26 | 27 | int max = BrokenCompute.max(7, 8, 3, 4, 5); 28 | int maxUnderMeditation = 0; 29 | 30 | // (@_@) 31 | 32 | // (^_^) 33 | 34 | assertThat(maxUnderMeditation, is(max)); 35 | } 36 | 37 | /** 38 | * Meditate on the runtime exception that can occur while calling the max method on BrokenCompute. 39 | */ 40 | @Koan 41 | public void reflectOnHowVarargsCanIntroduceRuntimeExceptions(){ 42 | 43 | Exception exceptionUnderMeditation = null; 44 | 45 | try { 46 | // (@_@) 47 | 48 | // (^_^) 49 | 50 | } catch (Exception ex){ 51 | exceptionUnderMeditation = ex; 52 | } 53 | 54 | assertThat(exceptionUnderMeditation, is(instanceOf(ArrayIndexOutOfBoundsException.class))); 55 | } 56 | 57 | 58 | /** 59 | * Meditate on explicitly handling the case where zero arguments are passed. 60 | */ 61 | @Koan 62 | public void reflectOnAddingExceptionHandlingForZeroArgument(){ 63 | 64 | String messageUnderMeditation = ""; 65 | 66 | // (@_@) 67 | 68 | // (^_^) 69 | 70 | try { 71 | ImprovedCompute.max(); 72 | } catch(Exception e){ 73 | assertThat(messageUnderMeditation, is(e.getMessage())); 74 | } 75 | 76 | } 77 | 78 | /** 79 | * Meditate on changing the method signature to require at least one argument of the required type 80 | */ 81 | @Koan 82 | public void reflectOnCallingAMethodThatRequiresAtLeastOneArgument(){ 83 | 84 | int max = BrokenCompute.max(7, 8, 3, 4, 5); 85 | int maxUnderMeditation = 0; 86 | 87 | // (@_@) 88 | 89 | // (^_^) 90 | 91 | assertThat(maxUnderMeditation, is(max)); 92 | } 93 | 94 | /** 95 | * Meditate on the time it take for each of the method calls. 96 | */ 97 | @Koan 98 | public void reflectOnArrayCreationCostCompute(){ 99 | // (@_@) 100 | // No meditation required 101 | // (^_^) 102 | 103 | long startTime = System.nanoTime(); 104 | int max = PerformantCompute.max(1); 105 | long endTime = System.nanoTime(); 106 | System.out.println("Number of Args 1: " + (endTime-startTime)); 107 | 108 | startTime = System.nanoTime(); 109 | max = PerformantCompute.max(1); 110 | endTime = System.nanoTime(); 111 | System.out.println("Number of Args 1: " + (endTime-startTime)); 112 | 113 | startTime = System.nanoTime(); 114 | max = PerformantCompute.max(1,2); 115 | endTime = System.nanoTime(); 116 | System.out.println("Number of Args 2: " + (endTime-startTime)); 117 | 118 | startTime = System.nanoTime(); 119 | max = PerformantCompute.max(1,2,3); 120 | endTime = System.nanoTime(); 121 | System.out.println("Number of Args 3: " + (endTime-startTime)); 122 | 123 | startTime = System.nanoTime(); 124 | max = PerformantCompute.max(1,2,3,4); 125 | endTime = System.nanoTime(); 126 | System.out.println("Number of Args 4: " + (endTime-startTime)); 127 | 128 | startTime = System.nanoTime(); 129 | max = PerformantCompute.max(1,2,3,4,5); 130 | endTime = System.nanoTime(); 131 | System.out.println("Number of Args 5: " + (endTime-startTime)); 132 | 133 | startTime = System.nanoTime(); 134 | max = PerformantCompute.max(1); 135 | endTime = System.nanoTime(); 136 | System.out.println("Number of Args 1: " + (endTime-startTime)); 137 | 138 | startTime = System.nanoTime(); 139 | max = PerformantCompute.max(1,2); 140 | endTime = System.nanoTime(); 141 | System.out.println("Number of Args 2: " + (endTime-startTime)); 142 | 143 | startTime = System.nanoTime(); 144 | max = PerformantCompute.max(1,2,3); 145 | endTime = System.nanoTime(); 146 | System.out.println("Number of Args 3: " + (endTime-startTime)); 147 | 148 | startTime = System.nanoTime(); 149 | max = PerformantCompute.max(1,2,3,4); 150 | endTime = System.nanoTime(); 151 | System.out.println("Number of Args 4: " + (endTime-startTime)); 152 | 153 | startTime = System.nanoTime(); 154 | max = PerformantCompute.max(1,2,3,4,5); 155 | endTime = System.nanoTime(); 156 | System.out.println("Number of Args 5: " + (endTime-startTime)); 157 | } 158 | 159 | /* 160 | Number of Args 1: 882422 161 | Number of Args 1: 366 162 | Number of Args 2: 1829 163 | Number of Args 3: 2194 164 | Number of Args 4: 3657 165 | Number of Args 5: 1097 166 | Number of Args 1: 366 167 | Number of Args 2: 365 168 | Number of Args 3: 366 169 | Number of Args 4: 732 170 | Number of Args 5: 731 171 | */ 172 | 173 | } 174 | 175 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/patterns/observer/ex1/ObserverKoan.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.patterns.observer.ex1; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import com.javakoan.lesson.patterns.observer.domain.Customer; 6 | import com.javakoan.lesson.patterns.observer.domain.Magazine; 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.runner.RunWith; 10 | 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.PrintStream; 13 | 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.core.Is.is; 16 | import static org.hamcrest.core.IsNot.not; 17 | import static org.hamcrest.core.StringContains.containsString; 18 | 19 | /** 20 | * Created with IntelliJ IDEA. 21 | * User: nicholas_smith 22 | * Date: 08/07/13 23 | * Time: 11:46 24 | * To change this template use File | Settings | File Templates. 25 | */ 26 | @RunWith(KoanRunner.class) 27 | public class ObserverKoan { 28 | 29 | private final ByteArrayOutputStream systemDotOutContent = new ByteArrayOutputStream(); 30 | 31 | private NewsKiosk newsKiosk; 32 | 33 | private final int MAGAZINE_EDITION = 2; 34 | private final String MAGAZINE_CONTENT = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; 35 | 36 | private final String TOM = "Tom"; 37 | private final String DICK = "Dick"; 38 | private final String HARRY = "Harry"; 39 | 40 | 41 | /** 42 | * Meditate on the outcome of a customer reading a magazine 43 | */ 44 | @Koan 45 | public void reflectOnTheOutcomeOfACustomerReadingAMagazine(){ 46 | Customer tom = new Customer(TOM); 47 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 48 | tom.read(magazine); 49 | 50 | String outputUnderMeditation = ""; 51 | 52 | /* (@_@) */ 53 | 54 | /* (^_^) */ 55 | 56 | assertThat(outputUnderMeditation, is(getSystemDotOutContent())); 57 | } 58 | 59 | /** 60 | * Meditate on the outcome of a new magazine being published when the argument based notify method is called 61 | * on the Subject 62 | */ 63 | @Koan 64 | public void reflectOnHowObserversAreNotifiedOfUpdatesUsingPush(){ 65 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 66 | 67 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 68 | 69 | /* (@_@) */ 70 | 71 | /* (^_^) */ 72 | 73 | assertThat(getSystemDotOutContent(), is(TOM + " is reading: " + MAGAZINE_CONTENT)); 74 | } 75 | 76 | /** 77 | * Meditate on the outcome of a new magazine being published when the zero argument notify method is called 78 | * on the Subject 79 | */ 80 | @Koan 81 | public void reflectOnHowObserversCanBeNotifiedUsingPullRequest(){ 82 | CollectionCustomer harry = new CollectionCustomer(HARRY, newsKiosk); 83 | 84 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 85 | 86 | /* (@_@) */ 87 | 88 | /* (^_^) */ 89 | 90 | assertThat(getSystemDotOutContent(), is(HARRY + " is reading: " + MAGAZINE_CONTENT)); 91 | } 92 | 93 | /** 94 | * Meditate on how the pattern supports notifying all interested parties 95 | */ 96 | @Koan 97 | public void reflectOnNotifyingAllInterestedParties(){ 98 | 99 | /* (@_@) */ 100 | 101 | /* (^_^) */ 102 | 103 | String tomIsReading = TOM + " is reading: " + MAGAZINE_CONTENT; 104 | String harryIsReading = HARRY + " is reading: " + MAGAZINE_CONTENT; 105 | 106 | assertThat(getSystemDotOutContent(), containsString(tomIsReading)); 107 | assertThat(getSystemDotOutContent(), containsString(harryIsReading)); 108 | } 109 | 110 | /** 111 | * Meditate on the type of news kiosk update that will distribute to Harry but not Tom 112 | */ 113 | @Koan 114 | public void reflectOnDifferentTypesOfCustomerWillReceiveDifferentServices(){ 115 | HomeDeliveryCustomer tom = new HomeDeliveryCustomer(TOM, newsKiosk); 116 | CollectionCustomer harry = new CollectionCustomer(HARRY, newsKiosk); 117 | 118 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 119 | 120 | /* (@_@) */ 121 | 122 | /* (^_^) */ 123 | 124 | String harryIsReading = HARRY + " is reading: " + MAGAZINE_CONTENT; 125 | String tomIsReading = TOM + " is reading: " + MAGAZINE_CONTENT; 126 | 127 | assertThat(getSystemDotOutContent(), containsString(harryIsReading)); 128 | assertThat(getSystemDotOutContent(), not(containsString(tomIsReading))); 129 | } 130 | 131 | /** 132 | * Meditate on the order in which observers are notified are 133 | */ 134 | @Koan 135 | public void reflectOnNotificationOrder(){ 136 | 137 | /* (@_@) */ 138 | 139 | /* (^_^) */ 140 | 141 | Magazine magazine = new Magazine(MAGAZINE_CONTENT, MAGAZINE_EDITION); 142 | newsKiosk.newMagazinePublished(magazine); 143 | 144 | String expectedOutput = TOM + " is reading: " + MAGAZINE_CONTENT + System.lineSeparator() + 145 | DICK + " is reading: " + MAGAZINE_CONTENT + System.lineSeparator() + 146 | HARRY + " is reading: " + MAGAZINE_CONTENT; 147 | 148 | assertThat(getSystemDotOutContent(), is(expectedOutput)); 149 | } 150 | 151 | @Before 152 | public void setUpSystemStreams(){ 153 | newsKiosk = new NewsKiosk(); 154 | System.setOut(new PrintStream(systemDotOutContent)); 155 | } 156 | 157 | @After 158 | public void cleanUpSystemStreams() { 159 | System.setOut(null); 160 | } 161 | 162 | private String getSystemDotOutContent(){ 163 | return systemDotOutContent.toString().trim(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/koan/java/com/javakoan/lesson/java/primitives/PrimitiveBasics.java: -------------------------------------------------------------------------------- 1 | package com.javakoan.lesson.java.primitives; 2 | 3 | import com.javakoan.fixture.KoanRunner; 4 | import com.javakoan.fixture.annotation.Koan; 5 | import org.junit.runner.RunWith; 6 | 7 | import static junit.framework.Assert.assertEquals; 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | import static org.hamcrest.core.Is.is; 10 | import static org.hamcrest.core.IsNot.not; 11 | import static org.junit.Assert.assertFalse; 12 | import static org.junit.Assert.assertTrue; 13 | import static org.junit.Assert.fail; 14 | 15 | /** 16 | * Created with IntelliJ IDEA. 17 | * User: nicholas_smith 18 | * Date: 29/05/13 19 | * Time: 10:50 20 | * To change this template use File | Settings | File Templates. 21 | */ 22 | 23 | @RunWith(KoanRunner.class) 24 | public class PrimitiveBasics { 25 | 26 | byte byte8Bit; short short16Bit; int int32Bit; long long64Bit; float float32Bit; double double64bit; 27 | char char16Bit; boolean bool; 28 | 29 | /** 30 | * Meditate on how to assign values to each primitive type so it is not longer represented by its default value 31 | */ 32 | @Koan 33 | public void reflectOnHowToAssignPrimitiveValuesWithLiteralsAndGainInsightIntoDefaultValues(){ 34 | 35 | /* (@_@) Begin Meditation */ 36 | 37 | /* (^_^) Complete Meditation */ 38 | 39 | assertThat(byte8Bit, is(not((byte)0))); 40 | assertThat(short16Bit, is(not((short)0))); 41 | assertThat(int32Bit, is(not(0))); 42 | assertThat(long64Bit, is(not((long)0))); 43 | assertThat(float32Bit, is(not((float)0))); 44 | assertThat(double64bit, is(not((double)0))); 45 | assertThat(char16Bit, is(not('\u0000'))); 46 | assertThat(bool, is(not(false))); 47 | } 48 | 49 | 50 | /** 51 | * Meditate on how to use constants in the Byte class to initialise maximum and minimum byte values 52 | */ 53 | @Koan 54 | public void reflectOnMinimumAndMaximumByteValues(){ 55 | byte maximumByteValue = 0; 56 | byte minimumByteValue = 0; 57 | 58 | /* (@_@) */ 59 | 60 | /* (^_^) */ 61 | 62 | assertThat(maximumByteValue, is((byte)127)); 63 | assertThat(minimumByteValue, is((byte)-128)); 64 | } 65 | 66 | 67 | /** 68 | * Meditate on the maximum and minimum values of integers and how it might be important for you digital persona: 69 | * http://www.escapistmagazine.com/forums/read/7.829855-GTA-V-2-147-483-647-Glitch-Could-Wipe-Your-Cash-Away 70 | * Also muse on 2^31-1 and 0x7fffffff 71 | */ 72 | @Koan 73 | public void reflectOnMinimumAndMaximumIntValues(){ 74 | 75 | int maximumIntValue = 0; 76 | int minimumIntValue = 0; 77 | 78 | /* (@_@) */ 79 | 80 | /* (^_^) */ 81 | 82 | assertThat(maximumIntValue, is(2147483647)); 83 | assertThat(minimumIntValue, is(-2147483648)); 84 | } 85 | 86 | /** 87 | * Meditate on how to use constants in the Double class to initialise maximum and minimum double values 88 | */ 89 | @Koan 90 | public void reflectOnMinimumAndMaximumDoubleValues(){ 91 | double maximumDoubleValue = 0; 92 | double minimumDoubleValue = 0; 93 | 94 | /* (@_@) */ 95 | 96 | /* (^_^) */ 97 | 98 | assertThat(maximumDoubleValue, is(1.7976931348623157e+308)); 99 | assertThat(minimumDoubleValue, is(4.9e-324)); 100 | } 101 | 102 | 103 | /** 104 | * Meditate on the character literals maximum and minimum values using the UTF character set 105 | */ 106 | @Koan 107 | public void reflectOnCharactersAlsoHaveMinimumAndMaximumValues(){ 108 | char maximumCharValue = '\u0001'; 109 | char minimumCharValue = '\u0001'; 110 | 111 | /* (@_@) */ 112 | 113 | /* (^_^) */ 114 | 115 | assertThat(maximumCharValue, is(Character.MAX_VALUE)); 116 | assertThat(minimumCharValue, is(Character.MIN_VALUE)); 117 | } 118 | 119 | /** 120 | * Meditate on what equality constitutes with regards to primitive types 121 | */ 122 | @Koan 123 | public void reflectOnEqualityForPrimitives(){ 124 | int valueA = 0; 125 | int valueB = 1; 126 | 127 | /* (@_@) */ 128 | /* (^_^) */ 129 | 130 | assertEquals(valueA, valueB); 131 | } 132 | 133 | /** 134 | * Meditate on what equality constitutes when comparing primitives and boxed primitives 135 | */ 136 | @Koan 137 | public void reflectOnBoxedPrimitivesAreUnBoxedForEvaluationWithPrimitive(){ 138 | Integer valueA = 555; 139 | int valueB = 999; 140 | 141 | /* (@_@) */ 142 | /* (^_^) */ 143 | 144 | assertTrue(valueA == valueB); 145 | assertTrue(valueA.intValue() == valueB); 146 | } 147 | 148 | /** 149 | * Meditate on what equality constitutes boxed primitives 150 | */ 151 | @Koan 152 | public void reflectOnEqualityForBoxedPrimitives(){ 153 | Double valueA = 1.00; 154 | Double valueB = 0.00; 155 | 156 | /* (@_@) */ 157 | /* (^_^) */ 158 | 159 | assertEquals(valueA, valueB); 160 | } 161 | 162 | /** 163 | * Mediate on integers having cached references between -128 to 127 164 | * http://tech.puredanger.com/2007/02/01/valueof/ 165 | */ 166 | @Koan 167 | public void reflectOnIntegersHaveCachedValues(){ 168 | Integer valueA = 1; 169 | Integer valueB = 0; 170 | 171 | /* (@_@) */ 172 | /* (^_^) */ 173 | 174 | assertTrue(valueA == valueB); 175 | } 176 | 177 | /** 178 | * Mediate on integers outside the range of -128 to 127 do not have the same object reference ... as one might expect 179 | */ 180 | @Koan 181 | public void reflectOnDifferentObjectIdsForBoxedPrimitives(){ 182 | Integer valueA = 200; 183 | Integer valueB = valueA; 184 | 185 | /* (@_@) */ 186 | /* (^_^) */ 187 | 188 | assertFalse(valueA == valueB); 189 | } 190 | 191 | /** 192 | * Meditate on the default initialization of objects as class variables and the potential dangers. 193 | * Hint: Prevent the fail method being called, by causing a null pointer exception. 194 | */ 195 | private Integer boxedInt; 196 | @Koan 197 | public void reflectOnNullPointersWhenUnboxingIntegers(){ 198 | 199 | try { 200 | /* (@_@) */ 201 | fail("Should have thrown null pointer"); 202 | /* (^_^) */ 203 | } catch (NullPointerException e){ 204 | assertTrue(e instanceof NullPointerException); 205 | } 206 | } 207 | 208 | /** 209 | * Meditate on the default initialization of primitives as class variables and the potential benefits 210 | * Hint: Prevent the fail method being called, by using a clause on the value of primitiveInt. 211 | */ 212 | private int primitiveInt; 213 | @Koan 214 | public void reflectOnSaferToFavouringAPrimitive(){ 215 | 216 | try { 217 | /* (@_@) */ 218 | fail("Prevent failure by placing this code in a clause using primitiveInt"); 219 | /* (^_^) */ 220 | } catch (Exception e){ 221 | fail("Should not have thrown an exception"); 222 | } 223 | } 224 | 225 | /** 226 | * Meditate on unnecessary boxing causes program execution to take longer. 227 | * Hint: Create a for loop using the boxed Long instance variable in the same way the primitive is used 228 | */ 229 | @Koan 230 | public void reflectOnUnnecessaryBoxingTakesALongTime(){ 231 | long sum = 0L; 232 | long sumStartTime = System.currentTimeMillis(); 233 | for (long i = 0; i < Integer.MAX_VALUE; i++) { 234 | sum += i; 235 | } 236 | long sumDuration = System.currentTimeMillis() - sumStartTime; 237 | 238 | Long boxedSum = 0L; 239 | long boxedSumStartTime = System.currentTimeMillis(); 240 | 241 | /* (@_@) */ 242 | 243 | /* (^_^) */ 244 | long boxedSumDuration = System.currentTimeMillis() - boxedSumStartTime; 245 | 246 | System.out.println("boxedSumDuration: " + boxedSumDuration + " sumDuration: " + sumDuration); 247 | 248 | assertTrue(boxedSumDuration > sumDuration); 249 | } 250 | 251 | } 252 | --------------------------------------------------------------------------------