├── .github └── workflows │ └── send_new_pr_email.yml ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── Design Patterns & Principles.png ├── LICENSE ├── README.md └── src ├── abstractfactory └── pizzastoreexample │ ├── PizzaTestDrive.kt │ ├── factory │ ├── NYPizzaIngredientFactory.kt │ └── PizzaIngredientFactory.kt │ ├── ingredients │ ├── Cheese.kt │ ├── Clams.kt │ ├── Dough.kt │ ├── Pepperoni.kt │ ├── Sauce.kt │ └── Veggies.kt │ ├── pizza │ ├── CheesePizza.kt │ ├── ClamPizza.kt │ └── Pizza.kt │ └── pizzastore │ ├── NYPizzaStore.kt │ └── PizzaStore.kt ├── adapter └── ducksimulator │ ├── DuckTestDrive.kt │ ├── adapter │ └── TurkeyAdapter.kt │ ├── duck │ ├── Duck.kt │ └── MallardDuck.kt │ └── turkey │ ├── Turkey.kt │ └── WildTurkey.kt ├── command ├── RemoteLoader.kt ├── RemoteLoaderWithUndo.kt ├── command │ ├── Command.kt │ ├── LightOffCommand.kt │ ├── LightOnCommand.kt │ ├── MacroCommand.kt │ ├── NoCommand.kt │ ├── StereoOffCommand.kt │ └── StereoOnWithCDCommand.kt ├── invoker │ ├── RemoteControl.kt │ └── RemoteControlWithUndo.kt └── receiver │ ├── Light.kt │ └── Stereo.kt ├── composite └── dinermenu │ ├── MenuTestDrive.kt │ ├── client │ └── Waitress.kt │ ├── iterable │ ├── Menu.kt │ ├── MenuComponent.kt │ └── MenuItem.kt │ └── iterator │ ├── CompositeIterator.kt │ └── NullIterator.kt ├── decorator └── coffeeshop │ ├── Beverage.kt │ ├── CoffeeShop.kt │ ├── CondimentDecorator.kt │ ├── beverages │ ├── Espresso.kt │ └── HouseBlend.kt │ └── condiments │ ├── Mocha.kt │ ├── Soy.kt │ └── Whip.kt ├── facade └── hometheaterexample │ ├── HomeTheaterTestDrive.kt │ ├── facade │ └── HomeTheaterFacade.kt │ └── subsystems │ ├── Amplifier.kt │ ├── DvdPlayer.kt │ ├── PopcornPopper.kt │ ├── Projector.kt │ ├── Screen.kt │ └── TheaterLights.kt ├── factorymethod └── pizzastoreexample │ ├── PizzaTestDrive.kt │ ├── pizza │ ├── ChicagoStyleCheesePizza.kt │ ├── NYStyleCheesePizza.kt │ └── Pizza.kt │ └── pizzastore │ ├── ChicagoPizzaStore.kt │ ├── NYPizzaStore.kt │ └── PizzaStore.kt ├── iterator └── dinermenu │ ├── inbuilt │ ├── MenuTestDrive.kt │ ├── client │ │ └── Waitress.kt │ ├── iterable │ │ └── DinerMenu.kt │ └── iterator │ │ └── DinerMenuIterator.kt │ └── scratch │ ├── MenuTestDrive.kt │ ├── client │ └── Waitress.kt │ ├── data │ └── MenuItem.kt │ ├── iterable │ └── DinerMenu.kt │ └── iterator │ ├── DinerMenuIterator.kt │ └── Iterator.kt ├── observer └── weatherdata │ ├── inbuilt │ ├── CurrentConditionsDisplay.kt │ ├── WeatherData.kt │ └── WeatherStation.kt │ └── scratch │ ├── CurrentConditionsDisplay.kt │ ├── DisplayElement.kt │ ├── Observer.kt │ ├── Subject.kt │ ├── WeatherData.kt │ └── WeatherStation.kt ├── proxy └── protectionproxy │ └── commandexecutor │ ├── ProtectionProxyPatternTest.kt │ ├── proxy │ └── CommandExecutorProxy.kt │ ├── realsubject │ └── CommandExecutorImpl.kt │ └── subject │ └── CommandExecutor.kt ├── singleton ├── kotlinobject │ └── Singleton.kt └── scratch │ └── Singleton.kt ├── state └── gumballmachine │ ├── GumballMachineTestDrive.kt │ ├── context │ └── GumballMachine.kt │ └── state │ ├── HasQuarterState.kt │ ├── NoQuarterState.kt │ ├── SoldOutState.kt │ ├── SoldState.kt │ ├── State.kt │ └── WinnerState.kt ├── strategy └── actiongame │ ├── Game.kt │ ├── behaviour │ └── weapon │ │ ├── AxeBehavior.kt │ │ ├── BowAndArrowBehavior.kt │ │ ├── KnifeBehavior.kt │ │ ├── SwordBehavior.kt │ │ └── WeaponBehavior.kt │ └── character │ ├── Character.kt │ ├── King.kt │ ├── Knight.kt │ ├── Queen.kt │ └── Troll.kt └── templatemethod └── caffeinebeverage ├── BeverageTestDrive.kt └── beverages ├── CaffeineBeverage.kt ├── Coffee.kt └── Tea.kt /.github/workflows/send_new_pr_email.yml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [opened] 4 | 5 | jobs: 6 | message_from_branch: 7 | runs-on: ubuntu-latest 8 | name: Send customised mail on PR open 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Get pull request info 13 | id: prinfo 14 | run: | 15 | echo ::set-output name=b_name::${{ github.event.pull_request.head.ref }} 16 | echo ::set-output name=pr_title::${{ github.event.pull_request.title }} 17 | echo ::set-output name=pr_url::${{ github.event.pull_request.html_url }} 18 | echo ${{ github.event.pull_request.html_url }} 19 | 20 | - name: Get the message 21 | id: premail 22 | uses: Devansh-Maurya/Get-Email-Message-From-Branch-Action@v3 23 | with: 24 | repo: ${{ github.repository }} 25 | groups: pattern,principle 26 | branch: ${{steps.prinfo.outputs.b_name}} 27 | pr-title: ${{ steps.prinfo.outputs.pr_title}} 28 | pr-url: ${{ steps.prinfo.outputs.pr_url}} 29 | 30 | - name: Print message body 31 | run: echo "The message body is ${{steps.premail.outputs.body}}" 32 | 33 | - name: Send mail 34 | uses: dawidd6/action-send-mail@v2 35 | with: 36 | server_address: smtp.gmail.com 37 | server_port: 465 38 | username: ${{ secrets.MAIL_USERNAME }} 39 | password: ${{ secrets.MAIL_PASSWORD }} 40 | subject: ${{ steps.premail.outputs.subject }} 41 | body: ${{ steps.premail.outputs.body }} 42 | to: devansh233@gmail.com 43 | from: ${{ secrets.MAIL_SENDER_NAME }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/shelf 3 | /confluence/target 4 | /dependencies/repo 5 | /android.tests.dependencies 6 | /dependencies/android.tests.dependencies 7 | /dist 8 | /local 9 | /gh-pages 10 | /ideaSDK 11 | /clionSDK 12 | /android-studio/sdk 13 | out/ 14 | /tmp 15 | workspace.xml 16 | *.versionsBackup 17 | /idea/testData/debugger/tinyApp/classes* 18 | /jps-plugin/testData/kannotator 19 | /js/js.translator/testData/out/ 20 | /js/js.translator/testData/out-min/ 21 | /js/js.translator/testData/out-pir/ 22 | .gradle/ 23 | build/ 24 | !**/src/**/build 25 | !**/test/**/build 26 | *.iml 27 | !**/testData/**/*.iml 28 | .idea/libraries/Gradle*.xml 29 | .idea/libraries/Maven*.xml 30 | .idea/artifacts/PILL_*.xml 31 | .idea/artifacts/KotlinPlugin.xml 32 | .idea/modules 33 | .idea/runConfigurations/JPS_*.xml 34 | .idea/runConfigurations/PILL_*.xml 35 | .idea/libraries 36 | .idea/modules.xml 37 | .idea/gradle.xml 38 | .idea/compiler.xml 39 | .idea/inspectionProfiles/profiles_settings.xml 40 | .idea/.name 41 | .idea/artifacts/dist_auto_* 42 | .idea/artifacts/dist.xml 43 | .idea/artifacts/ideaPlugin.xml 44 | .idea/artifacts/kotlinc.xml 45 | .idea/artifacts/kotlin_compiler_jar.xml 46 | .idea/artifacts/kotlin_plugin_jar.xml 47 | .idea/artifacts/kotlin_jps_plugin_jar.xml 48 | .idea/artifacts/kotlin_daemon_client_jar.xml 49 | .idea/artifacts/kotlin_imports_dumper_compiler_plugin_jar.xml 50 | .idea/artifacts/kotlin_main_kts_jar.xml 51 | .idea/artifacts/kotlin_compiler_client_embeddable_jar.xml 52 | .idea/artifacts/kotlin_reflect_jar.xml 53 | .idea/artifacts/kotlin_stdlib_js_ir_* 54 | .idea/artifacts/kotlin_test_js_ir_* 55 | .idea/artifacts/kotlin_stdlib_wasm_* 56 | .idea/jarRepositories.xml 57 | kotlin-ultimate/ 58 | node_modules/ 59 | .rpt2_cache/ 60 | libraries/tools/kotlin-test-js-runner/lib/ 61 | local.properties -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | xmlns:android 16 | 17 | ^$ 18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 26 | xmlns:.* 27 | 28 | ^$ 29 | 30 | 31 | BY_NAME 32 | 33 |
34 |
35 | 36 | 37 | 38 | .*:id 39 | 40 | http://schemas.android.com/apk/res/android 41 | 42 | 43 | 44 |
45 |
46 | 47 | 48 | 49 | .*:name 50 | 51 | http://schemas.android.com/apk/res/android 52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 | name 61 | 62 | ^$ 63 | 64 | 65 | 66 |
67 |
68 | 69 | 70 | 71 | style 72 | 73 | ^$ 74 | 75 | 76 | 77 |
78 |
79 | 80 | 81 | 82 | .* 83 | 84 | ^$ 85 | 86 | 87 | BY_NAME 88 | 89 |
90 |
91 | 92 | 93 | 94 | .* 95 | 96 | http://schemas.android.com/apk/res/android 97 | 98 | 99 | ANDROID_ATTRIBUTE_ORDER 100 | 101 |
102 |
103 | 104 | 105 | 106 | .* 107 | 108 | .* 109 | 110 | 111 | BY_NAME 112 | 113 |
114 |
115 |
116 |
117 |
118 |
119 |
-------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Design Patterns & Principles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Devansh-Maurya/Design-Patterns-And-Principles/6de47075205b7de24e1d19bb514b4a0c4f1c7cbd/Design Patterns & Principles.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Devansh Maurya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

A collection of design patterns and principles written in Kotlin

4 |

5 | 6 | ### What are Design Principles? 7 | 8 | **A design principle is a basic tool or technique that can be applied to designing or writing code to make that code more maintainable, flexible, or extensible.** 9 | 10 | [Explore the Design Principles in this repo](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles#design-principles) 11 | 12 | ### What are Design Patterns? 13 | 14 | **A design pattern is a solution to a problem in a context.** 15 | * The **context** is the situation in which the pattern applies. This should be a recurring situation. 16 | * The **problem** refers to the goal you are trying to achieve in this context, but it also refers to any constraints that occur in the context. 17 | * The **solution** is what you’re after: a general design that anyone can apply which resolves the goal and set of constraints. 18 | 19 | If you find yourself in a context with a problem that has a goal that is affected by a set of constraints, then you can apply a design that resolves the goal and constraints and leads to a solution. 20 | 21 | [Explore the Design Patterns in this repo](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles#design-patterns) 22 | 23 | ## Design Principles 24 | 25 | ### Index 26 | 27 | 1. Identify the aspects of your application that vary and separate them from what stays the same. 28 | 2. Program to an interface, not an implementation 29 | 3. Favor composition over inheritance 30 | 4. Strive for loosely coupled designs between objects that interact 31 | 5. The Open-Closed Principle 32 | 6. Dependency Inversion Principle 33 | 7. Principle of Least Knowledge (Law of Demeter) 34 | 8. The Hollywood Principle 35 | 9. Single Responsibility 36 | 37 | * **Identify the aspects of your application that vary and separate them from what stays the same.** Take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don’t. 38 | 39 | * **Program to an interface, not an implementation.** "Program to an interface" really means "Program to a supertype". The word interface is overloaded here. There’s the concept of interface, but there’s also the Java construct interface. You can program to an interface, without having to actually use a Java interface. The point is to exploit polymorphism by programming to a supertype so that the actual runtime object isn’t locked into the code. 40 | 41 | * **Favor composition over inheritance.** Creating systems using composition gives you a lot more flexibility. Not only does it let you encapsulate a family of algorithms into their own set of classes, but it also lets you change behavior at runtime as long as the object you’re composing with implements the correct behavior interface. 42 | 43 | * **Strive for loosely coupled designs between objects that interact.** Loosely coupled designs allow us to build flexible OO systems that can handle change because they minimize the interdependency between objects. 44 | 45 | * **The Open-Closed Principle: Classes should be open for extension, but closed for modification.** Allow classes to be easily extended to incorporate new behavior without modifying existing code. Such designs are resilient to change and flexible enough to take on new functionality to meet changing requirements. While it may seem like a contradiction, there are techniques for allowing code to be extended without direct modification. Be careful when choosing the areas of code that need to be extended; applying the Open-Closed Principle EVERYWHERE is wasteful and unnecessary, and can lead to complex, hard-to-understand code. 46 | 47 | * **Dependency Inversion Principle: Depend upon abstractions. Do not depend upon concrete classes.** At first, this principle sounds a lot like “Program to an interface, not an implementation,” right? It is similar; however, the Dependency Inversion Principle makes an even stronger statement about abstraction. It suggests that our high-level components should not depend on our low-level components; rather, they should both depend on abstractions. A “high-level” component is a class with behavior defined in terms of other, “low-level” components. For example, [PizzaStore](src/factorymethod/pizzastoreexample/pizzastore/PizzaStore.kt) is a high-level component because its behavior is defined in terms of pizzas - it creates all the different pizza objects, and prepares, bakes, cuts, and boxes them, while the pizzas it uses are low-level components. 48 | 49 | _**Where’s the “inversion” in Dependency Inversion Principle?**_ 50 | 51 | The “inversion” in the name Dependency Inversion Principle is there because it inverts the way you typically might think about your OO design. The low-level components depend on a higher level abstraction. Likewise, the high-level component is also tied to the same abstraction. So, the top-to-bottom dependency chart has inverted itself, with both high-level and low- level modules now depending on the abstraction. 52 | 53 | * **Principle of Least Knowledge (Law of Demeter): Talk only to your immediate friends** It means when you are designing a system, for any object, be careful of the number of classes it interacts with and also how it comes to interact with those classes. This principle prevents us from creating designs that have a large number of classes coupled together so that changes in one part of the system cascade to other parts. When you build a lot of dependencies between many classes, you are building a fragile system that will be costly to maintain and complex for others to understand. But how do you keep from doing this? The principle provides some guidelines: take any object; now from any method in that object, the principle tells us that we should only invoke methods that belong to: 54 | * The object itself 55 | * Objects passed in as a parameter to the method 56 | * Any object the method creates or instantiates (Notice that these guidelines tell us not to call methods on objects that were returned from calling other methods!!) 57 | * Any components of the object (Think of a “component” as any object that is referenced by an instance variable. In other words, think of this as a HAS-A relationship) 58 | 59 | What’s the harm in calling the method of an object we get back from another call? Well, if we were to do that, then we’d be making a request of another object’s subpart (and increasing the number of objects we directly know). In such cases, the principle forces us to ask the object to make the request for us; that way we don’t have to know about its component objects (and we keep our circle of friends small). 60 | 61 | * **The Hollywood Principle: Don't call us, we'll call you.** The Hollywood Principle gives us a way to prevent **"dependency rot."** Dependency rot happens when you have high-level components depending on low-level components depending on high-level components depending on sideways components depending on low-level components, and so on. When rot sets in, no one can easily understand the way a system is designed. With the Hollywood Principle, we allow low-level components to hook themselves into a system, but the high-level components determine when they are needed, and how. In other words, the high-level components give the low-level components a “don’t call us, we’ll call you” treatment. 62 | 63 | * **Single Responsibility: A class should have only one reason to change.** Every responsibility of a class is an area of potential change. More than one responsibility means more than one area of change. This principle guides us to keep each class to a single responsibility. We know we want to avoid change in a class like the plague — modifying code provides all sorts of opportunities for problems to creep in. Having two ways to change increases the probability the class will change in the future, and when it does, it’s going to affect two aspects of your design. Separating responsibility in design is one of the most difficult things to do. Our brains are just too good at seeing a set of behaviors and grouping them together even when there are actually two or more responsibilities. The only way to succeed is to be diligent in examining your designs and to watch out for signals that a class is changing in more than one way as your system grows. 64 | * **Cohesion** is a term you’ll hear used as a measure of how closely a class or a module supports a single purpose or responsibility. 65 | * We say that a module or class has high cohesion when it is designed around a set of related functions, and we say it has low cohesion when it is designed around a set of unrelated functions. 66 | * Cohesion is a more general concept than the Single Responsibility Principle, but the two are closely related. Classes that adhere to the principle tend to have high cohesion and are more maintainable than classes that take on multiple responsibilities and have low cohesion. 67 | 68 | ## Design Patterns 69 | 70 | ### Index 71 | 72 | 1. Strategy 73 | 2. Observer 74 | 3. Decorator 75 | 4. Factory Method 76 | 5. Abstract Factory 77 | 6. Singleton 78 | 7. Command 79 | 8. Adapter 80 | 9. Facade 81 | 10. Template Method 82 | 11. Iterator 83 | 12. Composite 84 | 13. State 85 | 14. Proxy 86 | 87 | * **Strategy Pattern:** The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently of clients that use it. 88 | * **Example:** [An action game design using Strategy pattern](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles/tree/master/src/strategy/actiongame) 89 | 90 | * **Observer Pattern: The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.** Subjects, or as we also know them, Observables, update Observers using a common interface. Observers are loosely coupled in that the Observable knows nothing about them, other than that they implement the Observer interface. You can push or pull data from the Observable when using the pattern (pull is considered more “correct”). Don’t depend on a specific order of notification for your Observers. Java has several implementations of the Observer Pattern, including the general purpose java.util.Observable. 91 | * **Example:** Weather app design using Observer pattern. 92 | 1. [From scratch](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles/tree/master/src/observer/weatherdata/scratch) 93 | 2. [Using Java's in-built Observer pattern](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles/tree/master/src/observer/weatherdata/inbuilt) 94 | 95 | * **Decorator Pattern: The Decorator Pattern attaches additional responsibilities to an object dynamically.Decorators provide a flexible alternative to subclassing for extending functionality.** Composition and delegation can often be used to add new behaviors at runtime. The Decorator Pattern provides an alternative to subclassing for extending behavior. The Decorator Pattern involves a set of decorator classes that are used to wrap concrete components. Decorator classes mirror the type of the components they decorate. (In fact, they are the same type as the components they decorate, either through inheritance or interface implementation.) Decorators change the behavior of their components by adding new functionality before and/or after (or even in place of) method calls to the component. You can wrap a component with any number of decorators. Decorators are typically transparent to the client of the component; that is, unless the client is relying on the component’s concrete type. Decorators can result in many small objects in our design, and overuse can be complex. 96 | * **Example:** [Making different beverages with add-ons using Decorator pattern](https://github.com/Devansh-Maurya/Design-Patterns-And-Principles/tree/master/src/decorator/coffeeshop) 97 | 98 | * **Factory Method Pattern: The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.** As with every factory, the Factory Method Pattern gives us a way to encapsulate the instantiations of concrete types. The abstract Creator gives you an interface with a method for creating objects, also known as the “factory method.” Any other methods implemented in the abstract Creator are written to operate on products produced by the factory method. Only subclasses actually implement the factory method and create products. As in the official definition, you’ll often hear developers say that the Factory Method lets subclasses decide which class to instantiate. They say “decide” not because the pattern allows subclasses themselves to decide at runtime, but because the creator class is written without knowledge of the actual products that will be created, which is decided purely by the choice of the subclass that is used. 99 | * **Example:** [PizzaStore creating Pizzas using Factory method pattern](src/factorymethod/pizzastoreexample) 100 | 101 | * **Abstract Factory Pattern: The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.** Abstract Factory allows a client to use an abstract interface to create a set of related products without knowing (or caring) about the concrete products that are actually produced. In this way, the client is decoupled from any of the specifics of the concrete products. Often the methods of an Abstract Factory are implemented as factory methods. The job of an Abstract Factory is to define an interface for creating a set of products. Each method in that interface is responsible for creating a concrete product, and we implement a subclass of the Abstract Factory to supply those implementations. So, factory methods are a natural way to implement your product methods in your abstract factories. 102 | * **Example:** [PizzaStore creating Pizzas with different ingredients using Abstract Factory pattern](src/abstractfactory/pizzastoreexample) 103 | 104 | * **Singleton Pattern: The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.** We’re taking a class and letting it manage a single instance of itself. We’re also preventing any other class from creating a new instance on its own. To get an instance, we have to go through the class itself. We’re also providing a global access point to the instance: whenever you need an instance, just query the class and it will hand you back the single instance. We can implement this so that the Singleton is created in a lazy manner, which is especially important for resource-intensive objects. 105 | * **Example:** 106 | 1. [From scratch](src/singleton/scratch) 107 | 2. [Using Kotlin's `object`](src/singleton/kotlinobject) 108 | 109 | * **Command Pattern: The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.** A **command object** encapsulates a request by binding together a set of actions on a specific **receiver**. To achieve this, it packages the actions and the receiver up into an object that exposes just one method, `execute()`. When called, `execute()` causes the actions to be invoked on the receiver. From the outside, no other objects really know what actions get performed on what receiver; they just know that if they call the `execute()` method, their request will be serviced. The examples linked below show parameterizing an object with a command. An **invoker** makes a request of a Command object by calling its `execute()` method, which invokes those actions on the receiver. Invokers can be parameterized with Commands, even dynamically at runtime. When commands support undo, they have an `undo()` method that mirrors the `execute()` method. Whatever `execute()` last did, `undo()` reverses. **Macro Commands** are a simple extension of Command that allow multiple commands to be invoked. Likewise, Macro Commands can easily support `undo()`. Commands may also be used to implement logging and transactional systems. 110 | * **Example:** A configurable remote control performing its actions using command objects 111 | 1. [Without undo on invoker](src/command/RemoteLoader.kt) 112 | 2. [With undo on invoker](src/command/RemoteLoaderWithUndo.kt) 113 | 114 | * **Adapter Pattern: The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.** This pattern allows us to use a client with an incompatible interface by creating an Adapter that does the conversion. This acts to decouple the client from the implemented interface, and if we expect the interface to change over time, the adapter encapsulates that change so that the client doesn't have to be modified each time it needs to operate against a different interface. The Adapter Pattern is full of good OO design principles. It makes use of object composition to wrap the **adaptee** with an altered interface. This approach has the added advantage that we can use an adapter with any subclass of the adaptee. The pattern binds the client to an interface, not an implementation; we could use several adapters, each converting a different backend set of classes. Or, we could add new implementations after the fact, as long as they adhere to the **Target interface.** Implementing an adapter may require little work or a great deal of work depending on the size and complexity of the target interface. There are two forms of the Adapter Pattern: **object** and **class adapters**. The example below is an example of **object adapter**. Class adapters require multiple inheritance and thus are not found in Kotlin (and Java). 115 | * **Example:** [Duck simulator using a Turkey in place of a duck using a `TurkeyAdapter`](src/adapter/ducksimulator) 116 | 117 | * **Facade Pattern: The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.** To use the Facade Pattern, we create a class that simplifies and unifies a set of more complex classes that belong to some subsystem. Unlike a lot of patterns, Facade is fairly straightforward. 118 | * **Example:** [Home Theater system's interface simplified by using a Facade pattern](src/facade/hometheaterexample) 119 | 120 | * **Template Method Pattern: The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.** This pattern is all about creating a template for an algorithm. A template is just a method that defines an algorithm as a set of steps. One or more of these steps is defined to be abstract and implemented by a subclass. This ensures the algorithm’s structure stays unchanged, while subclasses provide some part of the implementation. A hook is a method that is declared in the abstract class, but only given an empty or default implementation. This gives subclasses the ability to “hook into” the algorithm at various points, if they wish; a subclass is also free to ignore the hook. The Template Method Pattern gives us an important technique for code reuse. To prevent subclasses from changing the algorithm in the template method, declare the template method as final. The connection between the **Hollywood Principle** and the Template Method Pattern is probably somewhat apparent: when we design with the Template Method Pattern, we’re telling subclasses, “don’t call us, we’ll call you.” 121 | * **Example:** [Encapsulating Tea and Coffee preparation algorithms using Template method pattern](src/templatemethod/caffeinebeverage) 122 | 123 | * **Iterator Pattern: The Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.** the pattern gives you a way to step through the elements of an aggregate without having to know how things are represented under the covers. once you have a uniform way of accessing the elements of all your aggregate objects, you can write polymorphic code that works with any of these aggregates — just like the printMenu() method, which doesn’t care if the menu items are held in an Array or ArrayList (or anything else that can create an Iterator), as long as it can get hold of an Iterator. The other important impact on your design is that the Iterator Pattern takes the responsibility of traversing elements and gives that responsibility to the iterator object, not the aggregate object. This not only keeps the aggregate interface and implementation simpler, it removes the responsibility for iteration from the aggregate and keeps the aggregate focused on the things it should be focused on (managing a collection of objects), not on iteration. 124 | * **Example:** Iterating over a diner menu using iterator pattern. 125 | 1. [From scratch](src/iterator/dinermenu/scratch) 126 | 2. [Using Kotlin's Iterator and Iterable](src/iterator/dinermenu/inbuilt) 127 | 128 | * **Composite Pattern: The Composite Pattern allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.** The Composite Pattern allows us to build structures of objects in the form of trees that contain both compositions of objects and individual objects as nodes. Using a composite structure, we can apply the same operations over both composites and individual objects. In other words, in most cases we can ignore the differences between compositions of objects and individual objects. A composite contains **components**. Components come in two flavors: composites and leaf elements. Sound recursive? It is. A composite holds a set of children; those children may be other composites or leaf elements. When you organize data in this way you end up with a tree structure (actually an upside-down tree structure) with a composite at the root and branches of composites growing up to leaf nodes. There are many design tradeoffs in implementing Composite. You need to balance transparency and safety with your needs. 129 | * **Example:** [Printing diner menu example with submenus using Composite pattern combined with Iterator pattern](src/composite/dinermenu) 130 | 131 | * **State Pattern: he State Pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.** Because the pattern encapsulates state into separate classes and delegates to the object representing the current state, we know that behavior changes along with the internal state. The [Gumball Machine](src/state/gumballmachine/context/GumballMachine.kt) provides a good example: when the gumball machine is in the [NoQuarterState](src/state/gumballmachine/state/NoQuarterState.kt) and you insert a quarter, you get different behavior (the machine accepts the quarter) than if you insert a quarter when it’s in the [HasQuarterState](src/state/gumballmachine/state/HasQuarterState.kt) (the machine rejects the quarter). What does it mean for an object to “appear to change its class”? Think about it from the perspective of a client: if an object you’re using can completely change its behavior, then it appears to you that the object is actually instantiated from another class. In reality, however, you know that we are using composition to give the appearance of a class change by simply referencing different state objects. With the State Pattern, we have a set of behaviors encapsulated in state objects; at any time the context is delegating to one of those states. Over time, the current state changes across the set of state objects to reflect the internal state of the context, so the context’s behavior changes over time as well. The client usually knows very little, if anything, about the state objects. 132 | * **Example:** [GumballMachine with its states implemented using State pattern](src/state/gumballmachine) 133 | 134 | * **Proxy Pattern: The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.** Use the Proxy Pattern to create a representative object that controls access to another object, which may be remote, expensive to create, or in need of securing. there are many variations of the Proxy Pattern, and the variations typically revolve around the way the proxy "controls access." First we have a [`Subject`](src/proxy/protectionproxy/commandexecutor/subject), which provides an interface for the [`RealSubject`](src/proxy/protectionproxy/commandexecutor/realsubject) and the [`Proxy`](src/proxy/protectionproxy/commandexecutor/proxy). By implementing the same interface, the Proxy can be substituted for the RealSubject anywhere it occurs. The RealSubject is the object that does the real work. It’s the object that the Proxy represents and controls access to. The Proxy holds a reference to the RealSubject. In some cases, the Proxy may be responsible for creating and destroying the RealSubject. Clients interact with the RealSubject through the Proxy. Because the Proxy and RealSubject implement the same interface (Subject), the Proxy can be substituted anywhere the subject can be used. The Proxy also controls access to the RealSubject; this control may be needed if the Subject is running on a remote machine, if the Subject is expensive to create in some way or if access to the subject needs to be protected in some way. 135 | 136 | * **Example:** [CommandExecutor example using a Protection + Virtual Proxy](src/proxy/protectionproxy/commandexecutor) 137 | 138 | There are many variations of the Proxy Pattern, and the variations typically revolve around the way the proxy **"controls access."** 139 | 140 | 1. **Remote Proxy: Controls access to a remote object.** With Remote Proxy, the proxy acts as a local representative for an object that lives in a different JVM. A method call on the proxy results in the call being transferred over the wire, invoked remotely, and the result being returned back to the proxy and then to the Client. 141 | 2. **Virtual Proxy: Controls access to a resource that is expensive to create.** Virtual Proxy acts as a representative for an object that may be expensive to create. The Virtual Proxy often defers the creation of the object until it is needed; the Virtual Proxy also acts as a surrogate for the object before and while it is being created. After that, the proxy delegates requests directly to the RealSubject. 142 | 3. **Protection Proxy: Controls access to a resource based on access rights.** It’s a proxy that controls access to an object based on access rights. For instance, if we had an employee object, a Protection Proxy might allow the employee to call certain methods on the object, a manager to call additional methods (like setSalary()), and a human resources employee to call any method on the object. 143 | 4. **Firewall Proxy: Controls access to a set of network resources, protecting the subject from "bad" clients.** Often seen in corporate firewall systems. 144 | 5. **Smart Reference Proxy: Provides additional actions whenever a subject is referenced, such as counting the number of references to an object.** 145 | 6. **Caching Proxy: Provides temporary storage for results of operations that are expensive. It can also allow multiple clients to share the results to reduce computation or network latency.** Often seen in web server proxies as well as content management and publishing systems. 146 | 7. **Synchronization Proxy: Provides safe access to a subject from multiple threads.** Seen in JavaSpaces, where it controls synchronized access to an underlying set of objects in a distributed environment. 147 | 8. **Complexity Hiding Proxy: Hides the complexity of and controls access to a complex set of classes.** This is sometimes called the Facade Proxy for obvious reasons. The Complexity Hiding Proxy differs from the Facade Pattern in that the proxy controls access, while the Facade just provides an alternative interface. 148 | 9. **Copy-On-Write Proxy: Controls the copying of an object by deferring the copying of an object until it is required by a client.** This is a variant of the **Virtual Proxy**. Seen in Java's `CopyOnWriteArrayList`. 149 | 150 | Proxy is structurally similar to Decorator, but the two differ in their purpose. The Decorator Pattern adds behavior to an object, while a Proxy controls access. Like any wrapper, proxies will increase the number of classes and objects in your designs. -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/PizzaTestDrive.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample 2 | 3 | import abstractfactory.pizzastoreexample.pizzastore.NYPizzaStore 4 | 5 | 6 | /** 7 | * Created by devansh on 06/09/20. 8 | */ 9 | 10 | fun main() { 11 | val nyStore = NYPizzaStore() 12 | 13 | var pizza = nyStore.orderPizza("cheese") 14 | println("Ethan ordered a ${pizza?.name}\n") 15 | 16 | pizza = nyStore.orderPizza("clam") 17 | println("Joel ordered a ${pizza?.name}\n") 18 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/factory/NYPizzaIngredientFactory.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.factory 2 | 3 | import abstractfactory.pizzastoreexample.ingredients.* 4 | 5 | /** 6 | * Created by devansh on 05/09/20. 7 | */ 8 | 9 | class NYPizzaIngredientFactory : PizzaIngredientFactory { 10 | 11 | override fun createDough(): Dough = ThinCrustDough 12 | 13 | override fun createSauce(): Sauce = MarinaraSauce 14 | 15 | override fun createCheese(): Cheese = ReggianoCheese 16 | 17 | override fun createVeggies(): Array = 18 | arrayOf(Garlic, Onion, Mushroom, RedPepper) 19 | 20 | override fun createPepperoni(): Pepperoni = SlicedPepperoni 21 | 22 | override fun createClam(): Clams = FreshClams 23 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/factory/PizzaIngredientFactory.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.factory 2 | 3 | import abstractfactory.pizzastoreexample.ingredients.* 4 | 5 | /** 6 | * Created by devansh on 05/09/20. 7 | */ 8 | 9 | interface PizzaIngredientFactory { 10 | 11 | fun createDough(): Dough 12 | 13 | fun createSauce(): Sauce 14 | 15 | fun createCheese(): Cheese 16 | 17 | fun createVeggies(): Array 18 | 19 | fun createPepperoni(): Pepperoni 20 | 21 | fun createClam(): Clams 22 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Cheese.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Cheese 8 | 9 | object ReggianoCheese: Cheese() -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Clams.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Clams 8 | 9 | object FreshClams: Clams() -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Dough.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Dough 8 | 9 | object ThinCrustDough : Dough() 10 | 11 | -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Pepperoni.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Pepperoni 8 | 9 | object SlicedPepperoni: Pepperoni() -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Sauce.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Sauce 8 | 9 | object MarinaraSauce: Sauce() -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/ingredients/Veggies.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.ingredients 2 | 3 | /** 4 | * Created by devansh on 05/09/20. 5 | */ 6 | 7 | sealed class Veggies 8 | 9 | object Garlic: Veggies() 10 | 11 | object Onion: Veggies() 12 | 13 | object Mushroom: Veggies() 14 | 15 | object RedPepper: Veggies() 16 | 17 | -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/pizza/CheesePizza.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.pizza 2 | 3 | import abstractfactory.pizzastoreexample.factory.PizzaIngredientFactory 4 | 5 | /** 6 | * Created by devansh on 06/09/20. 7 | */ 8 | 9 | class CheesePizza(private val ingredientFactory: PizzaIngredientFactory) : Pizza() { 10 | 11 | override fun prepare() { 12 | println("Preparing $name") 13 | dough = ingredientFactory.createDough() 14 | sauce = ingredientFactory.createSauce() 15 | cheese = ingredientFactory.createCheese() 16 | } 17 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/pizza/ClamPizza.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.pizza 2 | 3 | import abstractfactory.pizzastoreexample.factory.PizzaIngredientFactory 4 | 5 | /** 6 | * Created by devansh on 06/09/20. 7 | */ 8 | 9 | class ClamPizza(private val pizzaIngredientFactory: PizzaIngredientFactory) : Pizza() { 10 | 11 | override fun prepare() { 12 | println("Preparing $name") 13 | dough = pizzaIngredientFactory.createDough() 14 | sauce = pizzaIngredientFactory.createSauce() 15 | cheese = pizzaIngredientFactory.createCheese() 16 | clam = pizzaIngredientFactory.createClam() 17 | } 18 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/pizza/Pizza.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.pizza 2 | 3 | import abstractfactory.pizzastoreexample.ingredients.* 4 | 5 | /** 6 | * Created by devansh on 06/09/20. 7 | */ 8 | 9 | abstract class Pizza { 10 | var name: String = "" 11 | var dough: Dough? = null 12 | var sauce: Sauce? = null 13 | val veggies: ArrayList = arrayListOf() 14 | var cheese: Cheese? = null 15 | var pepperoni: Pepperoni? = null 16 | var clam: Clams? = null 17 | 18 | abstract fun prepare() 19 | 20 | open fun bake() { 21 | println("Bake for 25 minutes at 350") 22 | } 23 | 24 | open fun cut() { 25 | println("Cutting the pizza into diagonal slices") 26 | } 27 | 28 | open fun box() { 29 | println("Place pizza in official PizzaStore box") 30 | } 31 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/pizzastore/NYPizzaStore.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.pizzastore 2 | 3 | import abstractfactory.pizzastoreexample.factory.NYPizzaIngredientFactory 4 | import abstractfactory.pizzastoreexample.factory.PizzaIngredientFactory 5 | import abstractfactory.pizzastoreexample.pizza.CheesePizza 6 | import abstractfactory.pizzastoreexample.pizza.ClamPizza 7 | import abstractfactory.pizzastoreexample.pizza.Pizza 8 | 9 | /** 10 | * Created by devansh on 06/09/20. 11 | */ 12 | 13 | class NYPizzaStore : PizzaStore() { 14 | 15 | private val ingredientFactory: PizzaIngredientFactory 16 | 17 | init { 18 | ingredientFactory = NYPizzaIngredientFactory() 19 | } 20 | 21 | override fun createPizza(type: String): Pizza? = 22 | when(type) { 23 | "cheese" -> { 24 | CheesePizza(ingredientFactory).apply { 25 | name = "New York Style Cheese Pizza" 26 | } 27 | } 28 | "clam" -> { 29 | ClamPizza(ingredientFactory).apply { 30 | name = "New York Style Clam Pizza" 31 | } 32 | } 33 | else -> null 34 | } 35 | } -------------------------------------------------------------------------------- /src/abstractfactory/pizzastoreexample/pizzastore/PizzaStore.kt: -------------------------------------------------------------------------------- 1 | package abstractfactory.pizzastoreexample.pizzastore 2 | 3 | import abstractfactory.pizzastoreexample.pizza.Pizza 4 | 5 | /** 6 | * Created by devansh on 06/09/20. 7 | */ 8 | 9 | abstract class PizzaStore { 10 | 11 | fun orderPizza(type: String): Pizza? = 12 | createPizza(type)?.apply { 13 | prepare() 14 | bake() 15 | cut() 16 | box() 17 | } 18 | 19 | protected abstract fun createPizza(type: String): Pizza? 20 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/DuckTestDrive.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator 2 | 3 | import adapter.ducksimulator.adapter.TurkeyAdapter 4 | import adapter.ducksimulator.duck.Duck 5 | import adapter.ducksimulator.duck.MallardDuck 6 | import adapter.ducksimulator.turkey.WildTurkey 7 | 8 | /** 9 | * Created by devansh on 12/09/20. 10 | */ 11 | 12 | fun main() { 13 | val duck = MallardDuck() 14 | 15 | val turkey = WildTurkey() 16 | val turkeyAdapter = TurkeyAdapter(turkey) 17 | 18 | println("The Turkey says...") 19 | turkey.run { 20 | gobble() 21 | fly() 22 | } 23 | 24 | println("\nThe Duck says...") 25 | testDuck(duck) 26 | 27 | println("\nThe TurkeyAdapter says") 28 | testDuck(turkeyAdapter) 29 | } 30 | 31 | private fun testDuck(duck: Duck) { 32 | duck.run { 33 | quack() 34 | fly() 35 | } 36 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/adapter/TurkeyAdapter.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator.adapter 2 | 3 | import adapter.ducksimulator.duck.Duck 4 | import adapter.ducksimulator.turkey.Turkey 5 | 6 | /** 7 | * Created by devansh on 12/09/20. 8 | */ 9 | 10 | class TurkeyAdapter(private val turkey: Turkey) : Duck { 11 | 12 | override fun quack() { 13 | turkey.gobble() 14 | } 15 | 16 | // Turkeys fly in short spurts - they can't do long-distance flying like ducks. 17 | // To map between a Duck's fly() method and a Turkey's, 18 | // we need to call Turkey's fly() method five times to make up for it. 19 | override fun fly() { 20 | repeat(5) { 21 | turkey.fly() 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/duck/Duck.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator.duck 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | interface Duck { 8 | 9 | fun quack() 10 | 11 | fun fly() 12 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/duck/MallardDuck.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator.duck 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class MallardDuck : Duck { 8 | 9 | override fun quack() { 10 | println("Quack") 11 | } 12 | 13 | override fun fly() { 14 | println("I'm flying") 15 | } 16 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/turkey/Turkey.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator.turkey 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | interface Turkey { 8 | 9 | fun gobble() 10 | 11 | fun fly() 12 | } -------------------------------------------------------------------------------- /src/adapter/ducksimulator/turkey/WildTurkey.kt: -------------------------------------------------------------------------------- 1 | package adapter.ducksimulator.turkey 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class WildTurkey : Turkey { 8 | 9 | override fun gobble() { 10 | println("Gobble gobble") 11 | } 12 | 13 | override fun fly() { 14 | println("I'm flying a short distance") 15 | } 16 | } -------------------------------------------------------------------------------- /src/command/RemoteLoader.kt: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import command.command.* 4 | import command.invoker.RemoteControl 5 | import command.receiver.Light 6 | import command.receiver.Stereo 7 | 8 | /** 9 | * Created by devansh on 12/09/20. 10 | */ 11 | 12 | fun main() { 13 | 14 | val remoteControl = RemoteControl() 15 | 16 | val livingRoomLight = Light("Living Room") 17 | val kitchenLight = Light("Kitchen") 18 | val stereo = Stereo("Living Room") 19 | 20 | val livingRoomLightOn = LightOnCommand(livingRoomLight) 21 | val livingRoomLightOff = LightOffCommand(livingRoomLight) 22 | 23 | val kitchenLightOn = LightOnCommand(kitchenLight) 24 | val kitchenLightOff = LightOffCommand(kitchenLight) 25 | 26 | val stereoOnWithCD = StereoOnWithCDCommand(stereo) 27 | val stereoOff = StereoOffCommand(stereo) 28 | 29 | val partyOnMacro = MacroCommand(listOf(livingRoomLightOn, stereoOnWithCD)) 30 | val partyOffMacro = MacroCommand(listOf(livingRoomLightOff, stereoOff)) 31 | 32 | remoteControl.run { 33 | setCommand(0, livingRoomLightOn, livingRoomLightOff) 34 | setCommand(1, kitchenLightOn, kitchenLightOff) 35 | setCommand(2, stereoOnWithCD, stereoOff) 36 | setCommand(3, partyOnMacro, partyOffMacro) 37 | 38 | println(this) 39 | 40 | onButtonPushed(0) 41 | offButtonPushed(0) 42 | onButtonPushed(1) 43 | offButtonPushed(1) 44 | onButtonPushed(2) 45 | offButtonPushed(2) 46 | 47 | println("--- Pushing Macro On ---") 48 | onButtonPushed(3) 49 | println("--- Pushing Macro Off ---") 50 | offButtonPushed(3) 51 | } 52 | } -------------------------------------------------------------------------------- /src/command/RemoteLoaderWithUndo.kt: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import command.command.LightOffCommand 4 | import command.command.LightOnCommand 5 | import command.invoker.RemoteControlWithUndo 6 | import command.receiver.Light 7 | 8 | /** 9 | * Created by devansh on 12/09/20. 10 | */ 11 | 12 | fun main() { 13 | 14 | val remoteControl = RemoteControlWithUndo() 15 | 16 | val livingRoomLight = Light("Living Room") 17 | val kitchenLight = Light("Kitchen") 18 | 19 | val livingRoomLightOn = LightOnCommand(livingRoomLight) 20 | val livingRoomLightOff = LightOffCommand(livingRoomLight) 21 | 22 | val kitchenLightOn = LightOnCommand(kitchenLight) 23 | val kitchenLightOff = LightOffCommand(kitchenLight) 24 | 25 | 26 | remoteControl.run { 27 | setCommand(0, livingRoomLightOn, livingRoomLightOff) 28 | setCommand(1, kitchenLightOn, kitchenLightOff) 29 | 30 | onButtonPushed(0) 31 | offButtonPushed(0) 32 | println(this) 33 | undoButtonPushed() 34 | 35 | onButtonPushed(1) 36 | offButtonPushed(1) 37 | println(this) 38 | undoButtonPushed() 39 | } 40 | } -------------------------------------------------------------------------------- /src/command/command/Command.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | interface Command { 8 | 9 | fun execute() 10 | 11 | fun undo() 12 | } -------------------------------------------------------------------------------- /src/command/command/LightOffCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | import command.receiver.Light 4 | 5 | /** 6 | * Created by devansh on 12/09/20. 7 | */ 8 | 9 | class LightOffCommand(private val light: Light) : Command { 10 | 11 | override fun execute() { 12 | light.off() 13 | } 14 | 15 | override fun undo() { 16 | light.on() 17 | } 18 | } -------------------------------------------------------------------------------- /src/command/command/LightOnCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | import command.receiver.Light 4 | 5 | /** 6 | * Created by devansh on 12/09/20. 7 | */ 8 | 9 | class LightOnCommand(private val light: Light) : Command { 10 | 11 | override fun execute() { 12 | light.on() 13 | } 14 | 15 | override fun undo() { 16 | light.off() 17 | } 18 | } -------------------------------------------------------------------------------- /src/command/command/MacroCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class MacroCommand(private val commands: List) : Command { 8 | 9 | override fun execute() { 10 | commands.forEach { 11 | it.execute() 12 | } 13 | } 14 | 15 | override fun undo() { 16 | commands.forEach { 17 | it.undo() 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/command/command/NoCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class NoCommand : Command { 8 | override fun execute() {} 9 | 10 | override fun undo() {} 11 | } -------------------------------------------------------------------------------- /src/command/command/StereoOffCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | import command.receiver.Stereo 4 | 5 | /** 6 | * Created by devansh on 12/09/20. 7 | */ 8 | 9 | class StereoOffCommand(private val stereo: Stereo) : Command { 10 | 11 | override fun execute() { 12 | stereo.off() 13 | } 14 | 15 | override fun undo() {} 16 | } -------------------------------------------------------------------------------- /src/command/command/StereoOnWithCDCommand.kt: -------------------------------------------------------------------------------- 1 | package command.command 2 | 3 | import command.receiver.Stereo 4 | 5 | /** 6 | * Created by devansh on 12/09/20. 7 | */ 8 | 9 | class StereoOnWithCDCommand(private val stereo: Stereo) : Command { 10 | 11 | override fun execute() { 12 | stereo.run { 13 | on() 14 | setCd() 15 | setVolume(11) 16 | } 17 | } 18 | 19 | override fun undo() { 20 | stereo.off() 21 | } 22 | } -------------------------------------------------------------------------------- /src/command/invoker/RemoteControl.kt: -------------------------------------------------------------------------------- 1 | package command.invoker 2 | 3 | import command.command.Command 4 | import command.command.NoCommand 5 | 6 | /** 7 | * Created by devansh on 12/09/20. 8 | */ 9 | 10 | class RemoteControl { 11 | 12 | private val onCommands: Array 13 | private val offCommands: Array 14 | 15 | init { 16 | val noCommand = NoCommand() 17 | onCommands = Array(7) { noCommand } 18 | offCommands = Array(7) { noCommand } 19 | } 20 | 21 | fun setCommand(slot: Int, onCommand: Command, offCommand: Command) { 22 | onCommands[slot] = onCommand 23 | offCommands[slot] = offCommand 24 | } 25 | 26 | fun onButtonPushed(slot: Int) { 27 | onCommands[slot].execute() 28 | } 29 | 30 | fun offButtonPushed(slot: Int) { 31 | offCommands[slot].execute() 32 | } 33 | 34 | override fun toString() = 35 | StringBuilder().apply { 36 | append("\n------Remote Control------\n") 37 | onCommands.zip(offCommands).forEachIndexed { index, pair -> 38 | append("[slot $index] ${pair.first.javaClass.simpleName}\t${pair.second.javaClass.simpleName}\n") 39 | } 40 | }.toString() 41 | } -------------------------------------------------------------------------------- /src/command/invoker/RemoteControlWithUndo.kt: -------------------------------------------------------------------------------- 1 | package command.invoker 2 | 3 | import command.command.Command 4 | import command.command.NoCommand 5 | 6 | /** 7 | * Created by devansh on 12/09/20. 8 | */ 9 | 10 | class RemoteControlWithUndo { 11 | 12 | private val onCommands: Array 13 | private val offCommands: Array 14 | private var undoCommand: Command 15 | 16 | init { 17 | val noCommand = NoCommand() 18 | onCommands = Array(7) { noCommand } 19 | offCommands = Array(7) { noCommand } 20 | undoCommand = noCommand 21 | } 22 | 23 | fun setCommand(slot: Int, onCommand: Command, offCommand: Command) { 24 | onCommands[slot] = onCommand 25 | offCommands[slot] = offCommand 26 | } 27 | 28 | fun onButtonPushed(slot: Int) { 29 | onCommands[slot].run { 30 | execute() 31 | undoCommand = this 32 | } 33 | } 34 | 35 | fun offButtonPushed(slot: Int) { 36 | offCommands[slot].run { 37 | execute() 38 | undoCommand = this 39 | } 40 | } 41 | 42 | fun undoButtonPushed() { 43 | undoCommand.undo() 44 | } 45 | 46 | override fun toString() = 47 | StringBuilder().apply { 48 | append("\n------Remote Control------\n") 49 | onCommands.zip(offCommands).forEachIndexed { index, pair -> 50 | append("[slot $index] ${pair.first.javaClass.simpleName}\t${pair.second.javaClass.simpleName}\n") 51 | } 52 | append("[undo] ${undoCommand.javaClass.simpleName}\n") 53 | }.toString() 54 | } -------------------------------------------------------------------------------- /src/command/receiver/Light.kt: -------------------------------------------------------------------------------- 1 | package command.receiver 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class Light(private val name: String) { 8 | 9 | fun on() { 10 | println("$name light is on") 11 | } 12 | 13 | fun off() { 14 | println("$name light is off") 15 | } 16 | } -------------------------------------------------------------------------------- /src/command/receiver/Stereo.kt: -------------------------------------------------------------------------------- 1 | package command.receiver 2 | 3 | /** 4 | * Created by devansh on 12/09/20. 5 | */ 6 | 7 | class Stereo(private val name: String) { 8 | 9 | fun on() { 10 | println("$name stereo is on") 11 | } 12 | 13 | fun off() { 14 | println("$name stereo is off") 15 | } 16 | 17 | fun setCd() { 18 | println("$name stereo is set for CD input") 19 | } 20 | 21 | fun setDvd() { 22 | println("$name stereo is set for DVD input") 23 | } 24 | 25 | fun setRadio() { 26 | println("$name stereo is set for radio input") 27 | } 28 | 29 | fun setVolume(volume: Int) { 30 | println("$name stereo volume set to $volume") 31 | } 32 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/MenuTestDrive.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu 2 | 3 | import composite.dinermenu.client.Waitress 4 | import composite.dinermenu.iterable.Menu 5 | import composite.dinermenu.iterable.MenuItem 6 | 7 | /** 8 | * Created by devansh on 26/09/20. 9 | */ 10 | 11 | fun main() { 12 | val pancakeMenu = Menu("PANCAKE HOUSE MENU", "Breakfast") 13 | val dinerMenu = Menu("DINER MENU", "Lunch") 14 | val cafeMenu = Menu("CAFE MENU", "Dinner") 15 | val dessertMenu = Menu("DESSERT MENU", "Dessert of course!") 16 | 17 | // Top-level menu 18 | val allMenus = Menu("ALL MENUS", "All menus combined").apply { 19 | add(pancakeMenu) 20 | add(dinerMenu) 21 | add(cafeMenu) 22 | } 23 | 24 | dinerMenu.apply { 25 | add(MenuItem( 26 | name = "Pasta", 27 | description = "Spaghetti with Marinara Sauce, and a slice of sourdough bread", 28 | isVegetarian = true, 29 | price = 3.89)) 30 | add(dessertMenu) 31 | add(MenuItem( 32 | name = "Apple Pie", 33 | description = "Apple pie wit a flakey crust, topped with vanilla icecream", 34 | isVegetarian = true, 35 | price = 1.59 36 | )) 37 | } 38 | 39 | val waitress = Waitress(allMenus) 40 | waitress.printMenu() 41 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/client/Waitress.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.client 2 | 3 | import composite.dinermenu.iterable.MenuComponent 4 | 5 | /** 6 | * Created by devansh on 26/09/20. 7 | */ 8 | 9 | class Waitress(private val allMenus: MenuComponent) { 10 | 11 | fun printMenu() { 12 | allMenus.print() 13 | } 14 | 15 | fun printVegetarianMenu() { 16 | val iterator = allMenus.iterator() 17 | 18 | println("\nVEGETARIAN MENU\n----") 19 | 20 | while (iterator.hasNext()) { 21 | iterator.next()?.let { 22 | try { 23 | if (it.isVegetarian) { 24 | it.print() 25 | } 26 | } catch (e: UnsupportedOperationException) {} 27 | } 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/iterable/Menu.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.iterable 2 | 3 | import composite.dinermenu.iterator.CompositeIterator 4 | 5 | /** 6 | * Created by devansh on 26/09/20. 7 | */ 8 | 9 | class Menu( 10 | override val name: String, 11 | override val description: String) : MenuComponent() { 12 | 13 | private val menuComponents: ArrayList = arrayListOf() 14 | 15 | private val iterator: CompositeIterator by lazy { CompositeIterator(menuComponents.iterator()) } 16 | 17 | override fun add(menuComponent: MenuComponent) { 18 | menuComponents.add(menuComponent) 19 | } 20 | 21 | override fun remove(menuComponent: MenuComponent) { 22 | menuComponents.remove(menuComponent) 23 | } 24 | 25 | override fun getChild(i: Int): MenuComponent = menuComponents[i] 26 | 27 | override fun print() { 28 | print("\n$name") 29 | println(", $description") 30 | println("----------------------") 31 | 32 | val iterator = menuComponents.iterator() 33 | 34 | while (iterator.hasNext()) { 35 | val menuComponent = iterator.next() 36 | menuComponent.print() 37 | } 38 | } 39 | 40 | override fun iterator(): Iterator = iterator 41 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/iterable/MenuComponent.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.iterable 2 | 3 | /** 4 | * Created by devansh on 26/09/20. 5 | */ 6 | 7 | abstract class MenuComponent : Iterable { 8 | 9 | open val name: String 10 | get() { throw UnsupportedOperationException() } 11 | 12 | open val description: String 13 | get() { throw UnsupportedOperationException() } 14 | 15 | open val price: Double 16 | get() { throw UnsupportedOperationException() } 17 | 18 | open val isVegetarian: Boolean 19 | get() { throw UnsupportedOperationException() } 20 | 21 | open fun add(menuComponent: MenuComponent) { 22 | throw UnsupportedOperationException() 23 | } 24 | 25 | open fun remove(menuComponent: MenuComponent) { 26 | throw UnsupportedOperationException() 27 | } 28 | 29 | open fun getChild(i: Int): MenuComponent { 30 | throw UnsupportedOperationException() 31 | } 32 | 33 | open fun print() { 34 | throw UnsupportedOperationException() 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/iterable/MenuItem.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.iterable 2 | 3 | import composite.dinermenu.iterator.NullIterator 4 | 5 | /** 6 | * Created by devansh on 26/09/20. 7 | */ 8 | 9 | class MenuItem( 10 | override val name: String, 11 | override val description: String, 12 | override val isVegetarian: Boolean, 13 | override val price: Double 14 | ) : MenuComponent() { 15 | 16 | override fun print() { 17 | print(" $name") 18 | if (isVegetarian) { print("(v)") } 19 | println(", $price") 20 | println("\t-- $description") 21 | } 22 | 23 | override fun iterator(): Iterator = NullIterator() 24 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/iterator/CompositeIterator.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.iterator 2 | 3 | import composite.dinermenu.iterable.MenuComponent 4 | import java.util.* 5 | 6 | /** 7 | * Created by devansh on 26/09/20. 8 | */ 9 | 10 | class CompositeIterator(iterator: Iterator) : Iterator { 11 | 12 | private val stack: Stack> = Stack() 13 | 14 | init { 15 | stack.push(iterator) 16 | } 17 | 18 | override fun hasNext(): Boolean = 19 | if (stack.empty()) { 20 | false 21 | } else { 22 | val iterator = stack.peek() 23 | if (iterator.hasNext().not()) { 24 | stack.pop() 25 | hasNext() 26 | } else { 27 | true 28 | } 29 | } 30 | 31 | override fun next(): MenuComponent? = 32 | if (hasNext()) { 33 | val iterator = stack.peek() 34 | val component = iterator.next() 35 | stack.push(component?.iterator()) 36 | component 37 | } else { 38 | null 39 | } 40 | } -------------------------------------------------------------------------------- /src/composite/dinermenu/iterator/NullIterator.kt: -------------------------------------------------------------------------------- 1 | package composite.dinermenu.iterator 2 | 3 | /** 4 | * Created by devansh on 26/09/20. 5 | */ 6 | 7 | class NullIterator : Iterator { 8 | 9 | override fun hasNext(): Boolean = false 10 | 11 | override fun next(): T? = null 12 | 13 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/Beverage.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop 2 | 3 | /** 4 | * Created by devansh on 28/08/20. 5 | */ 6 | 7 | abstract class Beverage { 8 | 9 | open val description: String = "Unknown beverage" 10 | 11 | abstract fun cost(): Double 12 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/CoffeeShop.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop 2 | 3 | import decorator.coffeeshop.beverages.Espresso 4 | import decorator.coffeeshop.beverages.HouseBlend 5 | import decorator.coffeeshop.condiments.Mocha 6 | import decorator.coffeeshop.condiments.Soy 7 | import decorator.coffeeshop.condiments.Whip 8 | 9 | /** 10 | * Created by devansh on 28/08/20. 11 | */ 12 | 13 | fun main() { 14 | 15 | val beverage = Espresso() 16 | 17 | println("${beverage.description} $${beverage.cost()}") 18 | 19 | val beverage2 = 20 | Whip( 21 | Mocha( 22 | Soy( 23 | HouseBlend() 24 | ) 25 | ) 26 | ) 27 | 28 | println("${beverage2.description} $${beverage2.cost()}") 29 | 30 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/CondimentDecorator.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop 2 | 3 | import decorator.coffeeshop.Beverage 4 | 5 | /** 6 | * Created by devansh on 28/08/20. 7 | */ 8 | 9 | abstract class CondimentDecorator : Beverage() -------------------------------------------------------------------------------- /src/decorator/coffeeshop/beverages/Espresso.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop.beverages 2 | 3 | import decorator.coffeeshop.Beverage 4 | 5 | /** 6 | * Created by devansh on 28/08/20. 7 | */ 8 | 9 | class Espresso : Beverage() { 10 | 11 | override val description: String = javaClass.simpleName 12 | 13 | override fun cost(): Double = 1.99 14 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/beverages/HouseBlend.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop.beverages 2 | 3 | import decorator.coffeeshop.Beverage 4 | 5 | /** 6 | * Created by devansh on 28/08/20. 7 | */ 8 | 9 | class HouseBlend : Beverage() { 10 | 11 | override val description: String = javaClass.simpleName 12 | 13 | override fun cost(): Double = .89 14 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/condiments/Mocha.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop.condiments 2 | 3 | import decorator.coffeeshop.Beverage 4 | import decorator.coffeeshop.CondimentDecorator 5 | 6 | /** 7 | * Created by devansh on 28/08/20. 8 | */ 9 | 10 | class Mocha(private val beverage: Beverage) : CondimentDecorator() { 11 | 12 | override val description: String 13 | get() = "${beverage.description}, Mocha" 14 | 15 | override fun cost(): Double = beverage.cost() + .20 16 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/condiments/Soy.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop.condiments 2 | 3 | import decorator.coffeeshop.Beverage 4 | import decorator.coffeeshop.CondimentDecorator 5 | 6 | /** 7 | * Created by devansh on 28/08/20. 8 | */ 9 | 10 | class Soy(private val beverage: Beverage) : CondimentDecorator() { 11 | 12 | override val description: String 13 | get() = "${beverage.description}, Soy" 14 | 15 | override fun cost(): Double = beverage.cost() + .15 16 | } -------------------------------------------------------------------------------- /src/decorator/coffeeshop/condiments/Whip.kt: -------------------------------------------------------------------------------- 1 | package decorator.coffeeshop.condiments 2 | 3 | import decorator.coffeeshop.Beverage 4 | import decorator.coffeeshop.CondimentDecorator 5 | 6 | /** 7 | * Created by devansh on 28/08/20. 8 | */ 9 | class Whip(private val beverage: Beverage) : CondimentDecorator() { 10 | 11 | override val description: String 12 | get() = "${beverage.description}, Whip" 13 | 14 | override fun cost(): Double = beverage.cost() + .10 15 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/HomeTheaterTestDrive.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample 2 | 3 | import facade.hometheaterexample.facade.HomeTheaterFacade 4 | import facade.hometheaterexample.subsystems.* 5 | 6 | /** 7 | * Created by devansh on 17/09/20. 8 | */ 9 | 10 | fun main() { 11 | val homeTheater = HomeTheaterFacade(Amplifier(), DvdPlayer(), Projector(), 12 | Screen(), TheaterLights(), PopcornPopper()) 13 | 14 | homeTheater.apply { 15 | watchMovie("Tamashaa") 16 | endMovie() 17 | } 18 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/facade/HomeTheaterFacade.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.facade 2 | 3 | import facade.hometheaterexample.subsystems.* 4 | 5 | /** 6 | * Created by devansh on 17/09/20. 7 | */ 8 | 9 | class HomeTheaterFacade(private val amp: Amplifier, 10 | private val dvd: DvdPlayer, 11 | private val projector: Projector, 12 | private val screen: Screen, 13 | private val lights: TheaterLights, 14 | private val popper: PopcornPopper) { 15 | 16 | fun watchMovie(movie: String) { 17 | println("Get ready to watch a movie...") 18 | popper.on() 19 | popper.pop() 20 | 21 | lights.dim(10) 22 | 23 | screen.down() 24 | 25 | projector.on() 26 | projector.wideScreenMode() 27 | 28 | amp.on() 29 | amp.setDvd(dvd) 30 | amp.setSurroundSound() 31 | amp.setVolume(5) 32 | 33 | dvd.on() 34 | dvd.play(movie) 35 | } 36 | 37 | fun endMovie() { 38 | println("Shutting movie theater down...") 39 | popper.off() 40 | 41 | lights.on() 42 | 43 | screen.up() 44 | 45 | projector.off() 46 | 47 | amp.off() 48 | 49 | dvd.stop() 50 | dvd.eject() 51 | dvd.off() 52 | } 53 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/Amplifier.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | class Amplifier { 7 | 8 | fun on() { 9 | println("Amplifier on") 10 | } 11 | 12 | fun setDvd(dvdPlayer: DvdPlayer) { 13 | println("Amplifier setting DVD player") 14 | } 15 | 16 | fun setSurroundSound() { 17 | println("Amplifier surround sound on (5 speakers, 1 subwoofer)") 18 | } 19 | 20 | fun setVolume(level: Int) { 21 | println("Amplifier setting volume to $level") 22 | } 23 | 24 | fun off() { 25 | println("Amplifier off") 26 | } 27 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/DvdPlayer.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | 7 | class DvdPlayer { 8 | 9 | private var currentMovie: String = "" 10 | 11 | fun on() { 12 | println("DVD Player on") 13 | } 14 | 15 | fun play(movie: String) { 16 | currentMovie = movie 17 | println("DVD Player playing \"$movie\"") 18 | } 19 | 20 | fun stop() { 21 | println("DVD Player stopped \"$currentMovie\"") 22 | } 23 | 24 | fun eject() { 25 | println("DVD Player eject") 26 | currentMovie = "" 27 | } 28 | 29 | fun off() { 30 | println("DVD Player off") 31 | } 32 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/PopcornPopper.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | 7 | class PopcornPopper { 8 | 9 | fun on() { 10 | println("Popcorn Popper on") 11 | } 12 | 13 | fun pop() { 14 | println("Popcorn Popper popping popcorn!") 15 | } 16 | 17 | fun off() { 18 | println("Popcorn Popper off") 19 | } 20 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/Projector.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | class Projector { 7 | 8 | fun on() { 9 | println("Projector on") 10 | } 11 | 12 | fun wideScreenMode() { 13 | println("Projector in widescreen mode (16x9 aspect ratio)") 14 | } 15 | 16 | fun off() { 17 | println("Projector off") 18 | } 19 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/Screen.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | class Screen { 7 | 8 | fun up() { 9 | 10 | } 11 | 12 | fun down() { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/facade/hometheaterexample/subsystems/TheaterLights.kt: -------------------------------------------------------------------------------- 1 | package facade.hometheaterexample.subsystems 2 | 3 | /** 4 | * Created by devansh on 17/09/20. 5 | */ 6 | class TheaterLights { 7 | 8 | fun dim(level: Int) { 9 | println("Theater Ceiling Lights dimming to $level%") 10 | } 11 | 12 | fun on() { 13 | println("Theater Ceiling Lights on") 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/PizzaTestDrive.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample 2 | 3 | import factorymethod.pizzastoreexample.pizzastore.ChicagoPizzaStore 4 | import factorymethod.pizzastoreexample.pizzastore.NYPizzaStore 5 | 6 | /** 7 | * Created by devansh on 02/09/20. 8 | */ 9 | 10 | fun main() { 11 | val nyStore = NYPizzaStore() 12 | val chicagoStore = ChicagoPizzaStore() 13 | 14 | var pizza = nyStore.orderPizza("cheese") 15 | println("Ethan ordered a ${pizza?.name}\n") 16 | 17 | pizza = chicagoStore.orderPizza("cheese") 18 | println("Joel ordered a ${pizza?.name}\n") 19 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizza/ChicagoStyleCheesePizza.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizza 2 | 3 | /** 4 | * Created by devansh on 02/09/20. 5 | */ 6 | 7 | class ChicagoStyleCheesePizza : Pizza() { 8 | 9 | override val name: String = "Chicago Style Deep Dish Cheese Pizza" 10 | override val dough: String = "Extra Thick Crust Dough" 11 | override val sauce: String = "Plum Tomato Sauce" 12 | 13 | init { 14 | toppings.add("Shredded Mozzarella Cheese") 15 | } 16 | 17 | override fun cut() { 18 | println("Cutting the pizza into square slices") 19 | } 20 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizza/NYStyleCheesePizza.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizza 2 | 3 | /** 4 | * Created by devansh on 02/09/20. 5 | */ 6 | 7 | class NYStyleCheesePizza : Pizza() { 8 | 9 | override val name: String = "NY Style Sauce and Cheese Pizza" 10 | override val dough: String = "Thin Crust Dough" 11 | override val sauce: String = "Marinara Sauce" 12 | 13 | init { 14 | toppings.add("Grated Reggiano Cheese") 15 | } 16 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizza/Pizza.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizza 2 | 3 | /** 4 | * Created by devansh on 02/09/20. 5 | */ 6 | 7 | abstract class Pizza { 8 | 9 | abstract val name: String 10 | abstract val dough: String 11 | abstract val sauce: String 12 | protected val toppings: ArrayList = arrayListOf() 13 | 14 | open fun prepare() { 15 | println("Preparing $name") 16 | println("Tossing dough...") 17 | println("Adding sauce...") 18 | println("Adding toppings: ") 19 | toppings.forEach { 20 | println(" $it") 21 | } 22 | } 23 | 24 | open fun bake() { 25 | println("Bake for 25 minutes at 350") 26 | } 27 | 28 | open fun cut() { 29 | println("Cutting the pizza into diagonal slices") 30 | } 31 | 32 | open fun box() { 33 | println("Place pizza in official PizzaStore box") 34 | } 35 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizzastore/ChicagoPizzaStore.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizzastore 2 | 3 | import factorymethod.pizzastoreexample.pizza.ChicagoStyleCheesePizza 4 | import factorymethod.pizzastoreexample.pizza.Pizza 5 | 6 | /** 7 | * Created by devansh on 02/09/20. 8 | */ 9 | class ChicagoPizzaStore : PizzaStore() { 10 | 11 | override fun createPizza(type: String): Pizza? = 12 | when(type) { 13 | "cheese" -> ChicagoStyleCheesePizza() 14 | else -> null 15 | } 16 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizzastore/NYPizzaStore.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizzastore 2 | 3 | import factorymethod.pizzastoreexample.pizza.NYStyleCheesePizza 4 | import factorymethod.pizzastoreexample.pizza.Pizza 5 | 6 | /** 7 | * Created by devansh on 02/09/20. 8 | */ 9 | 10 | class NYPizzaStore : PizzaStore() { 11 | 12 | override fun createPizza(type: String): Pizza? = 13 | when(type) { 14 | "cheese" -> NYStyleCheesePizza() 15 | else -> null 16 | } 17 | } -------------------------------------------------------------------------------- /src/factorymethod/pizzastoreexample/pizzastore/PizzaStore.kt: -------------------------------------------------------------------------------- 1 | package factorymethod.pizzastoreexample.pizzastore 2 | 3 | import factorymethod.pizzastoreexample.pizza.Pizza 4 | 5 | /** 6 | * Created by devansh on 02/09/20. 7 | */ 8 | 9 | abstract class PizzaStore { 10 | 11 | fun orderPizza(type: String): Pizza? = 12 | createPizza(type)?.apply { 13 | prepare() 14 | bake() 15 | cut() 16 | box() 17 | } 18 | 19 | protected abstract fun createPizza(type: String): Pizza? 20 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/inbuilt/MenuTestDrive.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.inbuilt 2 | 3 | import iterator.dinermenu.inbuilt.client.Waitress 4 | import iterator.dinermenu.inbuilt.iterable.DinerMenu 5 | 6 | /** 7 | * Created by devansh on 22/09/20. 8 | */ 9 | 10 | fun main() { 11 | val dinerMenu = DinerMenu() 12 | 13 | val waitress = Waitress(dinerMenu) 14 | waitress.printMenu() 15 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/inbuilt/client/Waitress.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.inbuilt.client 2 | 3 | import iterator.dinermenu.inbuilt.iterable.DinerMenu 4 | import iterator.dinermenu.scratch.data.MenuItem 5 | 6 | /** 7 | * Created by devansh on 21/09/20. 8 | */ 9 | 10 | class Waitress(private val dinerMenu: DinerMenu) { 11 | 12 | fun printMenu() { 13 | val dinerIterator = dinerMenu.iterator() 14 | 15 | println("MENU\n----\nBREAKFAST") 16 | printMenu(dinerIterator) 17 | } 18 | 19 | private fun printMenu(iterator: Iterator) { 20 | while (iterator.hasNext()) { 21 | val menuItem = iterator.next() 22 | println("${menuItem.name}, ") 23 | println("${menuItem.price} -- ") 24 | println(menuItem.description) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/inbuilt/iterable/DinerMenu.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.inbuilt.iterable 2 | 3 | import iterator.dinermenu.inbuilt.iterator.DinerMenuIterator 4 | import iterator.dinermenu.scratch.data.MenuItem 5 | 6 | /** 7 | * Created by devansh on 22/09/20. 8 | */ 9 | 10 | class DinerMenu : Iterable{ 11 | 12 | companion object { 13 | const val MAX_ITEMS = 6 14 | } 15 | 16 | private var numberOfItems = 0 17 | private val menuItems = arrayOfNulls(MAX_ITEMS) 18 | 19 | init { 20 | addItem("Vegetarian BLT", 21 | " (Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99) 22 | addItem("BLT", 23 | "Bacon with lettuce & tomato on whole wheat", false, 2.99) 24 | addItem("Soup of the day", 25 | "Soup of the day, with a side of potato salad", false, 3.29) 26 | addItem("Hotdog", 27 | "A hot dog with saurkraut, relish, onions, topped with cheese", false, 3.05) 28 | } 29 | 30 | fun addItem(name: String, description: String, vegetarian: Boolean, price: Double) { 31 | val menuItem = MenuItem(name, description, vegetarian, price) 32 | if (numberOfItems >= MAX_ITEMS) { 33 | System.err.println("Sorry, menu is full! Can't add item to menu") 34 | } else { 35 | menuItems[numberOfItems++] = menuItem 36 | } 37 | } 38 | 39 | override fun iterator(): Iterator = DinerMenuIterator(menuItems) 40 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/inbuilt/iterator/DinerMenuIterator.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.inbuilt.iterator 2 | 3 | import iterator.dinermenu.scratch.data.MenuItem 4 | 5 | /** 6 | * Created by devansh on 21/09/20. 7 | */ 8 | 9 | class DinerMenuIterator(private val list: Array) : MutableIterator { 10 | 11 | private var position = 0 12 | 13 | override fun hasNext(): Boolean { 14 | return position < list.size && list[position] != null 15 | } 16 | 17 | override fun next(): MenuItem { 18 | return list[position++] ?: throw IllegalStateException("Trying to access a position with no MenuItem") 19 | } 20 | 21 | override fun remove() { 22 | if (position <= 0) { 23 | throw IllegalStateException("You can't remove an item until you've done at least one next()") 24 | } 25 | if (list[position-1] != null) { 26 | for (i in (position - 1) until (list.size - 1)) { 27 | list[i] = list[i + 1] 28 | } 29 | list[list.size - 1] = null 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/MenuTestDrive.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch 2 | 3 | import iterator.dinermenu.scratch.client.Waitress 4 | import iterator.dinermenu.scratch.iterable.DinerMenu 5 | 6 | /** 7 | * Created by devansh on 21/09/20. 8 | */ 9 | 10 | fun main() { 11 | val dinerMenu = DinerMenu() 12 | 13 | val waitress = Waitress(dinerMenu) 14 | waitress.printMenu() 15 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/client/Waitress.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch.client 2 | 3 | import iterator.dinermenu.scratch.data.MenuItem 4 | import iterator.dinermenu.scratch.iterable.DinerMenu 5 | import iterator.dinermenu.scratch.iterator.Iterator 6 | 7 | /** 8 | * Created by devansh on 21/09/20. 9 | */ 10 | 11 | class Waitress(private val dinerMenu: DinerMenu) { 12 | 13 | fun printMenu() { 14 | val dinerIterator = dinerMenu.createIterator() 15 | 16 | println("MENU\n----\nBREAKFAST") 17 | printMenu(dinerIterator) 18 | } 19 | 20 | private fun printMenu(iterator: Iterator) { 21 | while (iterator.hasNext()) { 22 | val menuItem = iterator.next() 23 | println("${menuItem.name}, ") 24 | println("${menuItem.price} -- ") 25 | println(menuItem.description) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/data/MenuItem.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch.data 2 | 3 | /** 4 | * Created by devansh on 21/09/20. 5 | */ 6 | 7 | data class MenuItem( 8 | val name: String, 9 | val description: String, 10 | val vegetarian: Boolean, 11 | val price: Double 12 | ) -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/iterable/DinerMenu.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch.iterable 2 | 3 | import iterator.dinermenu.scratch.data.MenuItem 4 | import iterator.dinermenu.scratch.iterator.DinerMenuIterator 5 | import iterator.dinermenu.scratch.iterator.Iterator 6 | 7 | /** 8 | * Created by devansh on 21/09/20. 9 | */ 10 | 11 | class DinerMenu { 12 | 13 | companion object { 14 | const val MAX_ITEMS = 6 15 | } 16 | 17 | private var numberOfItems = 0 18 | private val menuItems = arrayOfNulls(MAX_ITEMS) 19 | 20 | init { 21 | addItem("Vegetarian BLT", 22 | " (Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99) 23 | addItem("BLT", 24 | "Bacon with lettuce & tomato on whole wheat", false, 2.99) 25 | addItem("Soup of the day", 26 | "Soup of the day, with a side of potato salad", false, 3.29) 27 | addItem("Hotdog", 28 | "A hot dog with saurkraut, relish, onions, topped with cheese", false, 3.05) 29 | } 30 | 31 | fun addItem(name: String, description: String, vegetarian: Boolean, price: Double) { 32 | val menuItem = MenuItem(name, description, vegetarian, price) 33 | if (numberOfItems >= MAX_ITEMS) { 34 | System.err.println("Sorry, menu is full! Can't add item to menu") 35 | } else { 36 | menuItems[numberOfItems++] = menuItem 37 | } 38 | } 39 | 40 | fun createIterator(): Iterator = DinerMenuIterator(menuItems) 41 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/iterator/DinerMenuIterator.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch.iterator 2 | 3 | import iterator.dinermenu.scratch.data.MenuItem 4 | 5 | /** 6 | * Created by devansh on 21/09/20. 7 | */ 8 | 9 | class DinerMenuIterator(private val items: Array) : Iterator { 10 | 11 | private var position = 0 12 | 13 | override fun hasNext(): Boolean { 14 | return position < items.size && items[position] != null 15 | } 16 | 17 | override fun next(): MenuItem { 18 | return items[position++]!! 19 | } 20 | } -------------------------------------------------------------------------------- /src/iterator/dinermenu/scratch/iterator/Iterator.kt: -------------------------------------------------------------------------------- 1 | package iterator.dinermenu.scratch.iterator 2 | 3 | /** 4 | * Created by devansh on 21/09/20. 5 | */ 6 | 7 | interface Iterator { 8 | 9 | fun hasNext(): Boolean 10 | 11 | fun next(): T 12 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/inbuilt/CurrentConditionsDisplay.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.inbuilt 2 | 3 | import observer.weatherdata.scratch.DisplayElement 4 | import java.util.* 5 | 6 | /** 7 | * Created by devansh on 26/08/20. 8 | */ 9 | 10 | class CurrentConditionsDisplay(observable: Observable) : Observer, DisplayElement { 11 | 12 | init { 13 | observable.addObserver(this) 14 | } 15 | 16 | override fun update(observable: Observable?, arg: Any?) { 17 | if (observable is WeatherData) { 18 | display(observable) 19 | } 20 | } 21 | 22 | override fun display(data: WeatherData) { 23 | println("Current conditions: ${data.temperature} F degrees and ${data.humidity}% humidity") 24 | } 25 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/inbuilt/WeatherData.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.inbuilt 2 | 3 | import java.util.Observable 4 | 5 | /** 6 | * Created by devansh on 26/08/20. 7 | */ 8 | 9 | class WeatherData : Observable() { 10 | 11 | var temperature: Float = 0f 12 | private set 13 | 14 | var humidity: Float = 0f 15 | private set 16 | 17 | var pressure: Float = 0f 18 | private set 19 | 20 | fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) { 21 | this.temperature = temperature 22 | this.humidity = humidity 23 | this.pressure = pressure 24 | onMeasurementsChanged() 25 | } 26 | 27 | private fun onMeasurementsChanged() { 28 | setChanged() 29 | notifyObservers() 30 | } 31 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/inbuilt/WeatherStation.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.inbuilt 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | fun main() { 8 | val weatherData = WeatherData() 9 | 10 | CurrentConditionsDisplay(weatherData) 11 | 12 | weatherData.setMeasurements(80f, 60f, 30.4f) 13 | weatherData.setMeasurements(82f, 70f, 29.2f) 14 | weatherData.setMeasurements(78f, 90f, 29.2f) 15 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/CurrentConditionsDisplay.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | class CurrentConditionsDisplay(weatherData: Subject) : Observer, DisplayElement { 8 | 9 | init { 10 | weatherData.registerObserver(this) 11 | } 12 | 13 | override fun update(data: WeatherData) { 14 | display(data) 15 | } 16 | 17 | override fun display(data: WeatherData) { 18 | println("Current conditions: ${data.temperature} F degrees and ${data.humidity}% humidity") 19 | } 20 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/DisplayElement.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | interface DisplayElement { 8 | fun display(data: T) 9 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/Observer.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | interface Observer { 8 | fun update(data: T) 9 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/Subject.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | interface Subject { 8 | 9 | fun registerObserver(observer: Observer) 10 | 11 | fun removeObserver(observer: Observer) 12 | 13 | fun notifyObservers() 14 | 15 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/WeatherData.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | class WeatherData : Subject { 8 | 9 | var temperature: Float = 0f 10 | private set 11 | 12 | var humidity: Float = 0f 13 | private set 14 | 15 | var pressure: Float = 0f 16 | private set 17 | 18 | private val observers: ArrayList> = arrayListOf() 19 | 20 | fun setMeasurements(temperature: Float, humidity: Float, pressure: Float) { 21 | this.temperature = temperature 22 | this.humidity = humidity 23 | this.pressure = pressure 24 | onMeasurementsChanged() 25 | } 26 | 27 | private fun onMeasurementsChanged() { 28 | notifyObservers() 29 | } 30 | 31 | override fun registerObserver(observer: Observer) { 32 | observers.add(observer) 33 | } 34 | 35 | override fun removeObserver(observer: Observer) { 36 | observers.remove(observer) 37 | } 38 | 39 | override fun notifyObservers() { 40 | observers.forEach { 41 | it.update(this) 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/observer/weatherdata/scratch/WeatherStation.kt: -------------------------------------------------------------------------------- 1 | package observer.weatherdata.scratch 2 | 3 | /** 4 | * Created by devansh on 26/08/20. 5 | */ 6 | 7 | fun main() { 8 | val weatherData = WeatherData() 9 | 10 | CurrentConditionsDisplay(weatherData) 11 | 12 | weatherData.setMeasurements(80f, 60f, 30.4f) 13 | weatherData.setMeasurements(82f, 70f, 29.2f) 14 | weatherData.setMeasurements(78f, 90f, 29.2f) 15 | } -------------------------------------------------------------------------------- /src/proxy/protectionproxy/commandexecutor/ProtectionProxyPatternTest.kt: -------------------------------------------------------------------------------- 1 | package proxy.protectionproxy.commandexecutor 2 | 3 | import proxy.protectionproxy.commandexecutor.proxy.CommandExecutorProxy 4 | 5 | /** 6 | * Created by devansh on 27/09/20. 7 | */ 8 | 9 | fun main() { 10 | 11 | val executor = CommandExecutorProxy("Maurya", "Devansh") 12 | try { 13 | executor.runCommand("ls -ltr") 14 | executor.runCommand(" rm -rf abc.pdf") 15 | } catch (e: Exception) { 16 | println("Exception message: ${e.message}") 17 | } 18 | } -------------------------------------------------------------------------------- /src/proxy/protectionproxy/commandexecutor/proxy/CommandExecutorProxy.kt: -------------------------------------------------------------------------------- 1 | package proxy.protectionproxy.commandexecutor.proxy 2 | 3 | import proxy.protectionproxy.commandexecutor.realsubject.CommandExecutorImpl 4 | import proxy.protectionproxy.commandexecutor.subject.CommandExecutor 5 | import javax.naming.NoPermissionException 6 | 7 | /** 8 | * Created by devansh on 27/09/20. 9 | */ 10 | 11 | class CommandExecutorProxy(user: String, password: String) : CommandExecutor { 12 | 13 | private val isAdmin: Boolean = user == "Devansh" && password == "Maurya" 14 | private val executor: CommandExecutor by lazy { CommandExecutorImpl() } /**Expensive to create object**/ 15 | 16 | override fun runCommand(cmd: String) { 17 | if (isAdmin) { 18 | executor.runCommand(cmd) 19 | } else { 20 | if (cmd.trim().startsWith("rm")) { 21 | throw NoPermissionException("rm command is not allowed for non-admin users.") 22 | } else { 23 | executor.runCommand(cmd) 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/proxy/protectionproxy/commandexecutor/realsubject/CommandExecutorImpl.kt: -------------------------------------------------------------------------------- 1 | package proxy.protectionproxy.commandexecutor.realsubject 2 | 3 | import proxy.protectionproxy.commandexecutor.subject.CommandExecutor 4 | 5 | /** 6 | * Created by devansh on 27/09/20. 7 | */ 8 | 9 | class CommandExecutorImpl : CommandExecutor { 10 | 11 | override fun runCommand(cmd: String) { 12 | Runtime.getRuntime().exec(cmd) 13 | println("'$cmd' command executed.") 14 | } 15 | } -------------------------------------------------------------------------------- /src/proxy/protectionproxy/commandexecutor/subject/CommandExecutor.kt: -------------------------------------------------------------------------------- 1 | package proxy.protectionproxy.commandexecutor.subject 2 | 3 | /** 4 | * Created by devansh on 27/09/20. 5 | */ 6 | 7 | interface CommandExecutor { 8 | 9 | fun runCommand(cmd: String) 10 | } -------------------------------------------------------------------------------- /src/singleton/kotlinobject/Singleton.kt: -------------------------------------------------------------------------------- 1 | package singleton.kotlinobject 2 | 3 | /** 4 | * Created by devansh on 06/09/20. 5 | */ 6 | 7 | object Singleton -------------------------------------------------------------------------------- /src/singleton/scratch/Singleton.kt: -------------------------------------------------------------------------------- 1 | package singleton.scratch 2 | 3 | /** 4 | * Created by devansh on 06/09/20. 5 | */ 6 | 7 | class Singleton private constructor() { 8 | 9 | companion object { 10 | @Volatile 11 | private var instance: Singleton? = null 12 | 13 | fun getInstance(): Singleton { 14 | if (instance == null) { 15 | synchronized(Singleton::class) { 16 | if (instance == null) { 17 | instance = Singleton() 18 | } 19 | } 20 | } 21 | return instance!! 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/GumballMachineTestDrive.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine 2 | 3 | import state.gumballmachine.context.GumballMachine 4 | 5 | /** 6 | * Created by devansh on 27/09/20. 7 | */ 8 | 9 | fun main() { 10 | val gumballMachine = GumballMachine(5) 11 | 12 | gumballMachine.apply { 13 | 14 | repeat(3) { 15 | insertQuarter() 16 | turnCrank() 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/context/GumballMachine.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.context 2 | 3 | import state.gumballmachine.state.* 4 | import kotlin.math.max 5 | 6 | /** 7 | * Created by devansh on 26/09/20. 8 | */ 9 | 10 | class GumballMachine(numberOfGumballs: Int) { 11 | 12 | val soldOutState: State 13 | val noQuarterState: State 14 | val hasQuarterState: State 15 | val soldState: State 16 | val winnerState: State 17 | 18 | var state: State 19 | 20 | var count: Int = numberOfGumballs 21 | private set 22 | 23 | init { 24 | soldOutState = SoldOutState() 25 | noQuarterState = NoQuarterState(this) 26 | hasQuarterState = HasQuarterState(this) 27 | soldState = SoldState(this) 28 | winnerState = WinnerState(this) 29 | 30 | count = numberOfGumballs 31 | 32 | state = if (numberOfGumballs > 0) { 33 | noQuarterState 34 | } else { 35 | soldOutState 36 | } 37 | } 38 | 39 | fun insertQuarter() { 40 | state.insertQuarter() 41 | } 42 | 43 | fun ejectQuarter() { 44 | state.ejectQuarter() 45 | } 46 | 47 | fun turnCrank() { 48 | state.turnCrank() 49 | state.dispense() 50 | } 51 | 52 | fun releaseBall() { 53 | println("A gumball comes rolling out the slot...") 54 | count = max(count - 1, 0) 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/HasQuarterState.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | import state.gumballmachine.context.GumballMachine 4 | import kotlin.random.Random 5 | 6 | /** 7 | * Created by devansh on 26/09/20. 8 | */ 9 | 10 | class HasQuarterState(private val gumballMachine: GumballMachine): State { 11 | 12 | private val randomWinner = Random(System.currentTimeMillis()) 13 | 14 | override fun insertQuarter() { 15 | println("You can't insert another quarter") 16 | } 17 | 18 | override fun ejectQuarter() { 19 | println("Quarter returned") 20 | gumballMachine.state = gumballMachine.noQuarterState 21 | } 22 | 23 | override fun turnCrank() { 24 | println("You turned...") 25 | val winner = randomWinner.nextInt(10) 26 | gumballMachine.state = if (winner == 0 && gumballMachine.count > 1) { 27 | gumballMachine.winnerState 28 | } else { 29 | gumballMachine.soldState 30 | } 31 | } 32 | 33 | override fun dispense() { 34 | println("No gumball dispensed") 35 | } 36 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/NoQuarterState.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | import state.gumballmachine.context.GumballMachine 4 | 5 | /** 6 | * Created by devansh on 26/09/20. 7 | */ 8 | 9 | class NoQuarterState(private val gumballMachine: GumballMachine) : State { 10 | 11 | override fun insertQuarter() { 12 | println("You inserted a quarter") 13 | gumballMachine.state = gumballMachine.hasQuarterState 14 | } 15 | 16 | override fun ejectQuarter() { 17 | println("You haven't inserted a quarter") 18 | } 19 | 20 | override fun turnCrank() { 21 | println("You turned, but there's no quarter") 22 | } 23 | 24 | override fun dispense() { 25 | println("You need to pay first") 26 | } 27 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/SoldOutState.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | /** 4 | * Created by devansh on 27/09/20. 5 | */ 6 | 7 | class SoldOutState : State { 8 | 9 | override fun insertQuarter() { 10 | println("You can't insert a quarter, the machine is sold out") 11 | } 12 | 13 | override fun ejectQuarter() { 14 | println("You can't eject, you haven't inserted a quarter") 15 | } 16 | 17 | override fun turnCrank() { 18 | println("You turned, but there are no gumballs") 19 | } 20 | 21 | override fun dispense() { 22 | println("No gumball dispensed") 23 | } 24 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/SoldState.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | import state.gumballmachine.context.GumballMachine 4 | 5 | /** 6 | * Created by devansh on 26/09/20. 7 | */ 8 | 9 | class SoldState(private val gumballMachine: GumballMachine): State { 10 | 11 | override fun insertQuarter() { 12 | println("Please wait, we're already giving you a gumball") 13 | } 14 | 15 | override fun ejectQuarter() { 16 | println("Sorry, you already turned the crank") 17 | } 18 | 19 | override fun turnCrank() { 20 | println("Turning twice doesn't get you another gumball!") 21 | } 22 | 23 | override fun dispense() { 24 | gumballMachine.releaseBall() 25 | if (gumballMachine.count > 0) { 26 | gumballMachine.state = gumballMachine.noQuarterState 27 | } else { 28 | println("Oops, out of gumballs!") 29 | gumballMachine.state = gumballMachine.soldOutState 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/State.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | /** 4 | * Created by devansh on 26/09/20. 5 | */ 6 | 7 | interface State { 8 | 9 | fun insertQuarter() 10 | 11 | fun ejectQuarter() 12 | 13 | fun turnCrank() 14 | 15 | fun dispense() 16 | 17 | } -------------------------------------------------------------------------------- /src/state/gumballmachine/state/WinnerState.kt: -------------------------------------------------------------------------------- 1 | package state.gumballmachine.state 2 | 3 | import state.gumballmachine.context.GumballMachine 4 | 5 | /** 6 | * Created by devansh on 27/09/20. 7 | */ 8 | 9 | class WinnerState(private val gumballMachine: GumballMachine) : State { 10 | 11 | override fun insertQuarter() { 12 | println("Please wait, we're already giving you a gumball") 13 | } 14 | 15 | override fun ejectQuarter() { 16 | println("Sorry, you already turned the crank") 17 | } 18 | 19 | override fun turnCrank() { 20 | println("Turning twice doesn't get you another gumball!") 21 | } 22 | 23 | override fun dispense() { 24 | gumballMachine.releaseBall() 25 | 26 | if (gumballMachine.count == 0) { 27 | gumballMachine.state = gumballMachine.soldOutState 28 | } else { 29 | gumballMachine.releaseBall() 30 | println("YOU'RE A WINNER! You got two gumballs for your quarter") 31 | gumballMachine.state = if (gumballMachine.count > 0) { 32 | gumballMachine.noQuarterState 33 | } else { 34 | println("Oops, out of gumballs!") 35 | gumballMachine.soldOutState 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/Game.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame 2 | 3 | import strategy.actiongame.behaviour.weapon.AxeBehavior 4 | import strategy.actiongame.behaviour.weapon.BowAndArrowBehavior 5 | import strategy.actiongame.behaviour.weapon.KnifeBehavior 6 | import strategy.actiongame.behaviour.weapon.SwordBehavior 7 | import strategy.actiongame.character.Character 8 | import strategy.actiongame.character.King 9 | import strategy.actiongame.character.Knight 10 | import strategy.actiongame.character.Queen 11 | import strategy.actiongame.character.Troll 12 | 13 | /** 14 | * Created by devansh on 25/08/20. 15 | */ 16 | 17 | fun main() { 18 | val king = King(SwordBehavior()) 19 | val queen = Queen(KnifeBehavior()) 20 | val knight = Knight(BowAndArrowBehavior()) 21 | val troll = Troll(AxeBehavior()) 22 | 23 | val characters = listOf(king, queen, knight, troll) 24 | 25 | characters.forEach(Character::fight) 26 | 27 | println("-----------------------------------") 28 | println("King and Knight swapping weapons...") 29 | println("-----------------------------------") 30 | 31 | king.weapon = BowAndArrowBehavior() 32 | knight.weapon = SwordBehavior() 33 | 34 | characters.forEach(Character::fight) 35 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/behaviour/weapon/AxeBehavior.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.behaviour.weapon 2 | 3 | /** 4 | * Created by devansh on 25/08/20. 5 | */ 6 | 7 | class AxeBehavior : WeaponBehavior { 8 | 9 | override fun useWeapon() { 10 | println("Chopping with an axe") 11 | } 12 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/behaviour/weapon/BowAndArrowBehavior.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.behaviour.weapon 2 | 3 | /** 4 | * Created by devansh on 25/08/20. 5 | */ 6 | 7 | class BowAndArrowBehavior : WeaponBehavior{ 8 | 9 | override fun useWeapon() { 10 | println("Shooting an arrow with a bow") 11 | } 12 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/behaviour/weapon/KnifeBehavior.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.behaviour.weapon 2 | 3 | /** 4 | * Created by devansh on 25/08/20. 5 | */ 6 | 7 | class KnifeBehavior : WeaponBehavior { 8 | 9 | override fun useWeapon() { 10 | println("Cutting with a knife") 11 | } 12 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/behaviour/weapon/SwordBehavior.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.behaviour.weapon 2 | 3 | /** 4 | * Created by devansh on 25/08/20. 5 | */ 6 | 7 | class SwordBehavior : WeaponBehavior { 8 | 9 | override fun useWeapon() { 10 | println("Swinging a sword") 11 | } 12 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/behaviour/weapon/WeaponBehavior.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.behaviour.weapon 2 | 3 | /** 4 | * Created by devansh on 25/08/20. 5 | */ 6 | 7 | interface WeaponBehavior { 8 | fun useWeapon() 9 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/character/Character.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.character 2 | 3 | import strategy.actiongame.behaviour.weapon.WeaponBehavior 4 | 5 | /** 6 | * Created by devansh on 25/08/20. 7 | */ 8 | 9 | open class Character(var weapon: WeaponBehavior) { 10 | 11 | open fun fight() { 12 | weapon.useWeapon() 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/character/King.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.character 2 | 3 | import strategy.actiongame.behaviour.weapon.WeaponBehavior 4 | 5 | /** 6 | * Created by devansh on 25/08/20. 7 | */ 8 | class King(weapon: WeaponBehavior) : Character(weapon) { 9 | 10 | override fun fight() { 11 | print("King is ") 12 | weapon.useWeapon() 13 | } 14 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/character/Knight.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.character 2 | 3 | import strategy.actiongame.behaviour.weapon.WeaponBehavior 4 | 5 | /** 6 | * Created by devansh on 25/08/20. 7 | */ 8 | 9 | class Knight(weapon: WeaponBehavior) : Character(weapon) { 10 | 11 | override fun fight() { 12 | print("Knight is ") 13 | weapon.useWeapon() 14 | } 15 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/character/Queen.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.character 2 | 3 | import strategy.actiongame.behaviour.weapon.WeaponBehavior 4 | 5 | /** 6 | * Created by devansh on 25/08/20. 7 | */ 8 | 9 | class Queen(weapon: WeaponBehavior) : Character(weapon) { 10 | 11 | override fun fight() { 12 | print("Queen is ") 13 | weapon.useWeapon() 14 | } 15 | } -------------------------------------------------------------------------------- /src/strategy/actiongame/character/Troll.kt: -------------------------------------------------------------------------------- 1 | package strategy.actiongame.character 2 | 3 | import strategy.actiongame.behaviour.weapon.WeaponBehavior 4 | 5 | /** 6 | * Created by devansh on 25/08/20. 7 | */ 8 | 9 | class Troll(weapon: WeaponBehavior) : Character(weapon) { 10 | 11 | override fun fight() { 12 | print("Troll is ") 13 | weapon.useWeapon() 14 | } 15 | } -------------------------------------------------------------------------------- /src/templatemethod/caffeinebeverage/BeverageTestDrive.kt: -------------------------------------------------------------------------------- 1 | package templatemethod.caffeinebeverage 2 | 3 | import templatemethod.caffeinebeverage.beverages.Coffee 4 | import templatemethod.caffeinebeverage.beverages.Tea 5 | 6 | /** 7 | * Created by devansh on 19/09/20. 8 | */ 9 | 10 | fun main() { 11 | 12 | val tea = Tea() 13 | 14 | val coffee = Coffee() 15 | 16 | println("\nMaking tea...") 17 | tea.prepareRecipe() 18 | 19 | println("\nMaking coffee...") 20 | coffee.prepareRecipe() 21 | } -------------------------------------------------------------------------------- /src/templatemethod/caffeinebeverage/beverages/CaffeineBeverage.kt: -------------------------------------------------------------------------------- 1 | package templatemethod.caffeinebeverage.beverages 2 | 3 | /** 4 | * Created by devansh on 19/09/20. 5 | */ 6 | 7 | abstract class CaffeineBeverage { 8 | 9 | fun prepareRecipe() { 10 | boilWater() 11 | brew() 12 | pourInCup() 13 | if (customerWantsCondiments()) { 14 | addCondiments() 15 | } 16 | } 17 | 18 | abstract fun brew() 19 | 20 | abstract fun addCondiments() 21 | 22 | fun boilWater() { 23 | println("Boiling water") 24 | } 25 | 26 | fun pourInCup() { 27 | println("Pouring into cup") 28 | } 29 | 30 | // hook 31 | open fun customerWantsCondiments(): Boolean = true 32 | } -------------------------------------------------------------------------------- /src/templatemethod/caffeinebeverage/beverages/Coffee.kt: -------------------------------------------------------------------------------- 1 | package templatemethod.caffeinebeverage.beverages 2 | 3 | import java.io.IOException 4 | 5 | /** 6 | * Created by devansh on 19/09/20. 7 | */ 8 | 9 | class Coffee : CaffeineBeverage() { 10 | 11 | override fun brew() { 12 | println("Dripping Coffee through filter") 13 | } 14 | 15 | override fun addCondiments() { 16 | println("Adding Sugar and Milk") 17 | } 18 | 19 | override fun customerWantsCondiments(): Boolean = 20 | getUserInput().startsWith("y", ignoreCase = true) 21 | 22 | private fun getUserInput(): String { 23 | var answer: String? = null 24 | 25 | println("Would you like milk and sugar with your coffee (y/n)? ") 26 | 27 | try { 28 | answer = readLine() 29 | } catch (ioe: IOException) { 30 | System.err.println("IO error trying to read your answer") 31 | } 32 | 33 | return answer ?: "no" 34 | } 35 | } -------------------------------------------------------------------------------- /src/templatemethod/caffeinebeverage/beverages/Tea.kt: -------------------------------------------------------------------------------- 1 | package templatemethod.caffeinebeverage.beverages 2 | 3 | import java.io.IOException 4 | 5 | /** 6 | * Created by devansh on 19/09/20. 7 | */ 8 | 9 | class Tea : CaffeineBeverage() { 10 | 11 | override fun brew() { 12 | println("Steeping the tea") 13 | } 14 | 15 | override fun addCondiments() { 16 | println("Adding Lemon") 17 | } 18 | 19 | override fun customerWantsCondiments(): Boolean = 20 | getUserInput().startsWith("y", ignoreCase = true) 21 | 22 | private fun getUserInput(): String { 23 | var answer: String? = null 24 | 25 | println("Would you like lemon with your tea (y/n)? ") 26 | 27 | try { 28 | answer = readLine() 29 | } catch (ioe: IOException) { 30 | System.err.println("IO error trying to read your answer") 31 | } 32 | 33 | return answer ?: "no" 34 | } 35 | } --------------------------------------------------------------------------------