├── .gitignore ├── .settings └── .gitignore ├── README.md └── modules ├── Module-00.md ├── Module-01.md ├── Module-02.md ├── Module-03.md ├── Module-04.md ├── Module-05.md ├── Module-06.md ├── Module-07.md ├── Module-08.md ├── Module-09.md ├── Module-10.md ├── answers ├── Answers-01.md ├── Answers-02.md ├── Answers-03.md ├── Answers-04.md ├── Answers-05.md ├── Answers-06.md ├── Answers-08.md ├── Answers-09.md ├── EnumeratedCard.java ├── Hand.java ├── MultiDeck.java ├── m01-a-multideck.png ├── m02-8.png ├── m03-1a.png ├── m03-2.png ├── m05-1.png ├── m05-2.png ├── m05-3.png ├── m05-3.sequence.jet ├── m05-4.png ├── m05-8.png ├── m05-9.png ├── m06-2.png ├── m06-2b.png ├── m06-4a.png ├── m06-4b.png ├── m08-1.png └── m08-2.png ├── artifacts ├── module-01 │ └── comp303 │ │ ├── Card1.java │ │ ├── Card2.java │ │ ├── Card3.java │ │ ├── Card4.java │ │ ├── Card5.java │ │ ├── Card6.java │ │ ├── Card7.java │ │ └── Client.java ├── module-02 │ └── comp303m02 │ │ ├── Card.java │ │ ├── Client.java │ │ ├── Deck.java │ │ └── Hand.java ├── module-04 │ └── module04 │ │ └── TestMath.java ├── module-06 │ └── module6 │ │ └── LuckyNumber.java ├── module-08 │ └── module08 │ │ ├── DefaultVisitor.java │ │ ├── IVisitable.java │ │ ├── McGill.java │ │ ├── OrgNode.java │ │ ├── PrintVisitor.java │ │ ├── SearchVisitor.java │ │ └── Visitor.java ├── module-09 │ └── module9 │ │ ├── DistributedComputation.java │ │ ├── IndecisivePerson.java │ │ ├── JoiningHands.java │ │ ├── PrintLotsOfNumbers.java │ │ └── RandomLoop.java └── module-10 │ └── lecture1 │ ├── DeserializationException.java │ ├── Product1.java │ ├── Product1a.java │ ├── Product2.java │ ├── Product2a.java │ ├── Product3.java │ ├── Product4.java │ ├── SerializationExampleBinary1.java │ ├── SerializationExampleCSV1.java │ ├── SerializationExampleCSV2.java │ ├── SerializationExampleCSVa.java │ ├── SerializationExampleJSON1.java │ ├── SerializationExampleJSON2.java │ ├── SerializationExampleXML1.java │ └── pretty.json └── figures ├── m01-DesignSpace.png ├── m01-objectDiagram1.png ├── m01-objectDiagram2.png ├── m01-scopes.png ├── m02-classDiagramExample1.png ├── m02-classDiagramNotation.png ├── m02-isp1.png ├── m02-isp2.png ├── m02-iterator1.png ├── m02-iterator2.png ├── m02-strategy1.png ├── m02-strategy2.png ├── m03-closure.png ├── m03-debugger.png ├── m03-objectDiagram1.png ├── m03-stateDiagram.png ├── m03-stateDiagramEx1.png ├── m04-CFG.png ├── m04-Stubs.png ├── m04-TestSuiteOrganization.png ├── m05-aggregation.png ├── m05-command.png ├── m05-composite1.png ├── m05-composite2.png ├── m05-compositedecorator.png ├── m05-decorator-sequence.png ├── m05-decorator1.png ├── m05-demeter1.png ├── m05-demeter2.png ├── m05-demeter3.png ├── m05-exercise1.png ├── m05-sequencedemo.png ├── m05-toolbar.png ├── m06-ComponentGraph.png ├── m06-JavaFX.png ├── m06-ObserverSummary.png ├── m06-basicObserver.png ├── m06-callbacks1.png ├── m06-completeObserver.png ├── m06-cs1.png ├── m06-cs2.png ├── m06-cs3.png ├── m06-cs4.png ├── m06-cs5.png ├── m06-cs6.png ├── m06-dependencies.png ├── m06-framework-a.png ├── m06-framework-b.png ├── m06-luckyNumber.png ├── m07-abstract.png ├── m07-fieldinit.png ├── m07-inheritance.png ├── m07-polymorphism.png ├── m07-template.png ├── m08-FileSystem.png └── m08-robot.png /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /.settings/.gitignore: -------------------------------------------------------------------------------- 1 | /org.eclipse.core.resources.prefs 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Software Design with Java 2 | 3 | --- 4 | 5 | **This version of the text is no longer maintained:** The material in this repository is an early draft of a manuscript that eventually matured into a finished textbook: [Introduction to Software Design with Java](https://www.springer.com/gp/book/9783030240936) published on July 17 2019. It is also available as an ebook on [SpringerLink](https://link.springer.com/book/10.1007/978-3-030-24094-3). This site will remain available indefinitely for use as initially licensed. The code and related resources for the book are publicly available on [this GitHub repository](https://github.com/prmr/DesignBook). 6 | 7 | --- 8 | 9 | This textbook provides an in-depth introduction to software design, with a focus on object-oriented design, and using the Java programming language. Compared with other resources for learning software design, this material is intended to have the following features: 10 | 11 | * **Concrete:** The concepts presented are worked down to a level where they are directly applied in source code. For this reason, a minimum level of Java programming proficiency is necessary. It is important to note that the Java programming language is the learning tool that allows me to illustrate and discuss various design concepts: it is not the subject being taught. Learning software design in-depth requires the use of a programming language, but the knowledge gained is expected translate easily to other languages. This being said, we might as well choose a mature, free, and well-supported language. Translation of the material to use [Plankalkül](https://en.wikipedia.org/wiki/Plankalk%C3%BCl) is left as an exercise. 12 | * **Narrative:** This text follows a narrative style that links design problems, concepts, and solutions into a cohesive package. This is in contrast to reference material such as design pattern catalogs or API documentation. 13 | * **Foundational:** *To the extent possible*, this material attempts to be independent from any specific technological solution, and in particular software application frameworks. Frameworks are invaluable for realistic development, but their continual evolution means that idiosyncratic knowledge required to use them has a short expectation of usefulness. Rather, this text focuses on general principles and techniques that underlie most frameworks. 14 | 15 | ## Module 0 - Preparation 16 | 17 | How does one approach a software development project? Start hacking and hope for the best? Probably not. This would just be [inviting disaster](http://spectrum.ieee.org/static/the-staggering-impact-of-it-systems-gone-wrong). In this pseudo-module, I will present an overview of the important steps we need to take to prepare to develop quality software, from understanding the problem to mapping out a development process to choosing and setting up our tools to sketching an application. 18 | 19 | [Module Notes](modules/Module-00.md) 20 | 21 | ## Module 1 - Encapsulation 22 | 23 | One of the major motivation for good design is to keep control of the complexity of a system. An idea that is paramount to simplifying the design of software is encapsulation. Ideally, classes serve to support functionality, but also to isolate distinct computations. This module will cover the most important concepts and techniques necessary to design well-encapsulated classes. 24 | 25 | [Module Notes](modules/Module-01.md) 26 | 27 | ## Module 2 - Types and Polymorphism 28 | 29 | One of the main mechanisms at our disposal for designing object-oriented applications is *polymorphism*, which in the case of Java and similar languages is heavily tied to the notion of a type system. In this module I will do a brief review of the foundations of polymorphism, introduce the idea of interface programming, and present common low-level design problems that are easily solved with polymorphism. 30 | 31 | [Module Notes](modules/Module-02.md) 32 | 33 | ## Module 3 - Object State 34 | 35 | The most difficult thing to reason about when looking at a program is state changes. What operations can have a side-effect, on which path can data flow, what impacts what? Is this module, I will clarify what object state is and how we can manage to keep control over its state in a principled way so we don't let the genie out of the bottle every time we instantiate an object. 36 | 37 | [Module Notes](modules/Module-03.md) 38 | 39 | ## Module 4 - Unit Testing 40 | 41 | How can we have confidence that our code is working properly? Every time we write or change a line of code, we could be introducing a lethal bug. *Unit testing* is a practice wherein we automatically execute our code to check that it does what we think it should. With unit testing, we can build a possibly large collection of tests that can quickly be run, for instance every time we change the code, to make sure we didn't break anything that used to work. In this module, I will introduce mechanisms that facilitate unit testing (reflection and type annotations) and provide you with basic techniques for designing unit tests and evaluating their quality. 42 | 43 | [Module Notes](modules/Module-04.md) 44 | 45 | ## Module 5 - Composition 46 | 47 | Large programs are typically assembled from smaller parts. In object-oriented programming this is done through two main mechanism: *composition* and *inheritance*. Composition simply means that one object holds a reference to another object and delegates some tasks to it. Although this sounds simple enough, unprincipled composition can lead to a terrible mess of spaghetti code. In this module I will give a quick refresher on the mechanism of polymorphism and how it can be used to elegantly compose objects together by following some well-known design patterns. Note that the second way of assembling systems is through inheritance, which is more complex and will be covered in Module 7. 48 | 49 | [Module Notes](modules/Module-05.md) 50 | 51 | ## Module 6 - Inversion of Control 52 | 53 | The idea of inversion of control is one of the most powerful intellectual tools in a software designer tool-box. It allows us to build incredibly sophisticated applications while keeping the overall design complexity down to a manageable level. The Observer pattern is extremely common in software development, and it's realized by most GUI toolkits on most software development platforms, from desktop to web to mobile applications. This module is dedicated to the principle of inversion of control (IoC) and its realization in the Observer pattern, also called the Model-View Controller (MVC) pattern. 54 | 55 | [Module Notes](modules/Module-06.md) 56 | 57 | ## Module 7 - Inheritance 58 | 59 | *Inheritance* is a programming language-supported mechanism that allows us to assemble state and computation from different classes into a single object. It is a powerful feature that offers a natural solution to many design problems related to code extensibility and dynamic configuration. At the same time, it is a complex mechanism that can all too easily be misused. In this module, I will offer a review of inheritance and present the major design rules and patterns involving it. 60 | 61 | [Module Notes](modules/Module-07.md) 62 | 63 | ## Module 8 - Design Patterns 64 | 65 | This module will explore design solutions that incorporate design patterns an inheritance and introduce the Visitor design pattern. 66 | 67 | [Module Notes](modules/Module-08.md) 68 | 69 | ## Module 9 - Concurrency 70 | 71 | *Concurrent programming* is a paradigm that allows developers to write code that can execute in parallel (on multi-core systems) or with the illusion of parallelism (on single-core ones). Concurrent programming is very, very challenging and is ideally only used in support of well-defined and well-understood design problems, such as performing background operations or displaying animations. In fact, many typical concurrent programming problems already have a framework-based solution that hides the use of concurrency primitives. In this module, I will present the foundations of object-oriented concurrent programming and a few examples of the use of concurrency in practice. 72 | 73 | [Module Notes](modules/Module-09.md) 74 | 75 | ## Module 10 - Serialization 76 | 77 | Sometimes, the data in a running program needs to be transferred out of the program, for example to be stored in a file or transmitted over a network. In this module, I will cover the main techniques for serializing object graphs using a variety of frameworks including Java's binary serialization and JavaBeans frameworks and JSON serialization APIs. This module will also feature an introduction to the agile development practice called refactoring. 78 | 79 | [Module Notes](modules/Module-10.md) 80 | 81 | ## Acknowledgment 82 | 83 | I have taught software design for close to a decade using Cay Hostmanns' book "Object-Oriented Design and Pattern, 2nd edition" (Wiley 2006). Although I have progressively developed my own way to introduce and present the material, Horstmann's text was a crucial influence in this progression. The part of module 4 on test case selection and structural testing was adapted from a lecture originally created by Andreas Zeller based on material from the book "Software Testing and Analysis: Process, Principles, and Techniques", by Pezze & Young (Wiley, 2008). 84 | By now the content of this repository has been scrutinized by hundreds of bright eyes and sharp minds (in roughly 2 to 1 proportions). I am grateful to those who have taken the time to report errors and suggest improvement: 85 | [Nima Adibpour](https://github.com/nima200), 86 | [Nicholas Lee](https://github.com/nicoalee), 87 | [Vasu Nadella](https://github.com/vasu), 88 | [Ashley Stendel](https://github.com/ashley-stendel), 89 | [Allan Wang](https://github.com/AllanWang), 90 | [dreaming-dog](https://github.com/dreaming-dog) 91 | 92 | 93 | ## License 94 | 95 | Creative Commons License 96 | 97 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 98 | 99 | Copyright Martin P. Robillard 2017 100 | -------------------------------------------------------------------------------- /modules/Module-00.md: -------------------------------------------------------------------------------- 1 | # Module 0 - Preparation 2 | 3 | ## Description 4 | 5 | How does one approach a software development project? Start hacking and hope for the best? Probably not. This would just be [inviting disaster](http://spectrum.ieee.org/static/the-staggering-impact-of-it-systems-gone-wrong). In this pseudo-module, I will present an overview of the important steps we need to take to prepare to develop quality software, from understanding the problem to mapping out a development process to choosing and setting up our tools to sketching an application. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Know about the concept of software process and the role it plays in software development; 12 | * Have a development environment set up. 13 | 14 | ## Notes 15 | 16 | Before jumping into software design, it's important to understand what software design is, and the place it occupies in the more general context of software development. 17 | 18 | There are many definitions of software design, each with a different focus. For example the [Wikipedia](https://en.wikipedia.org/wiki/Software_design) definition (as of 6 Sep 2017), is rather comprehensive, covering everything from requirements and problem domain analysis to aspects of the user experience. I personally prefer the definition offered by the book [Software Designers in Action](http://dl.acm.org/citation.cfm?id=2535028) because it emphasizes the close relation between design and code, which is one of the tenets of this course. 19 | 20 | > In its basic form, software design concerns determining the abstraction structure of the intended software. However, the abstraction structures in software design tend to be deeper than those in other disciplines, whether an expressive domain such as dance, or an engineering domain such as electronics. The problem is that the *behavior* of the code must be designed as much, if not more so, than the structure of the code in terms of its overall components and connectors, or classes and associations. As a result, the final design of a program is its source code, as even just minor variations in the code can radically alter its behavior. Because of this, the distinction between design and manufacture is blurred, make software design a highly interwoven process, with design and implementation in a continuous and close relationship - van der Hoek and Petre 2014 21 | 22 | As for the question of where software design fits in, the important thing to know is that design is only one of the many activities that take place during the development of a software system. There is an abundant literature on different [process models](https://en.wikipedia.org/wiki/Software_development_process) for software development. Basically a process model describes (and sometimes prescribes) how the different "steps" required to create a system are organized, and different process models offer different ways of doing things for different reasons. In the early days of the software engineering discipline it was believed that a "planning-heavy" process, exemplified by the [Waterfall model](https://en.wikipedia.org/wiki/Software_development_process#Waterfall_development), was the desirable way to build high-quality software. However, in the mid-1990s this belief was challenged by a movement towards a more self-organizing approach to software development, also called [agile development](https://en.wikipedia.org/wiki/Agile_software_development). In practice ideas about how to best develop software keep evolving, and in the end the important things are to have a process in the first place, and for that process to be well-adapted to the type of systems being developed and the organization that develops it, although that in itself is not so easy to achieve or even evaluate. 23 | 24 | Acquiring knowledge of and experience with a software development process is not the main focus of this book. These concerns would normally be the target of a software project-based course. However, general knowledge on software processes is good to have to be oriented in the general and confusing realm of software development. One concept of the software development process literature that is however relevant to software design is the idea of a *software development practice*. In the context of software development, a practice is a well-understood way of doing something to achieve a certain software development benefit. A practice many programmers are familiar with is "version control". You may also have heard of ["pair programming"](https://en.wikipedia.org/wiki/Pair_programming). In the book I will introduce and refer to a number of software development practices that directly support good design, including [Refactoring](https://en.wikipedia.org/wiki/Code_refactoring) and the use of [coding conventions](https://en.wikipedia.org/wiki/Coding_conventions). The following [subway map of agile practices](https://www.agilealliance.org/agile101/subway-map-to-agile-practices/) offers a neat visualizations of a sample of software development practices. 25 | 26 | ## Exercises 27 | 28 | The goal for this module is to set up your development environment that will allow you to complete the exercises and try the demos. 29 | 30 | 1. Ensure that you have a working version of [Eclipse for Java](http://www.eclipse.org/) and [Java 8](https://www.java.com/en/) 31 | 2. Install the Checkstyle Eclipse Plug-in. In Eclipse, select `Help | Eclipse Marketplace...` and enter `Checkstyle` in the `Find:` box. From the results, install `Checkstyle Plug-In...`. 32 | 3. Similar as the above, install the EclEmma Eclipse-Plug-In. Note that in the more recent versions of Eclipse, it is possible that EclEmma is already installed in the default distribution. 33 | 4. Clone the [SoftwareDesignCode](https://github.com/prmr/SoftwareDesignCode) project into your Eclipse workspace. In the Eclipse package explorer, right-click and select `Import... | Git -> Projects from Git | Clone URI` and under URI enter `https://github.com/prmr/SoftwareDesignCode`, then click-through, requesting to import existing Eclipse projects. Once imported, the project should compile without errors. 34 | 5. Just like you did for step 4, clone the [JetUML](https://github.com/prmr/JetUML) project into your Eclipse workspace. For convenience, you can also download the [executable file](http://cs.mcgill.ca/~martin/jetuml/) and place it somewhere convenient. 35 | 6. Just like you did for steps 4 and 5, clone the [Solitaire](https://github.com/prmr/Solitaire) project into your Eclipse workspace. 36 | 37 | To ensure that everything works: 38 | 39 | 1. In the SoftwareDesignCode project, in package `...demo`, right-click on the file `Welcome.java` and select `Run As... | Java Application`. You should see a small GUI application appear. Try the different buttons which should do the obvious thing. 40 | 2. Right-click on the file `TestAlternatingLabelProvider.java` and select `Run As... | JUnit Test`. A green bar should appear. 41 | 3. Right-click again on the file `TestAlternatingLabelProvider.java` and select `Coverage As... | JUnit Test`. A green bar should appear, along with a view showing coverage information. If you go back to the source code file, most lines should be highlighted in green. The meaning of this will be explained in class in a later module. 42 | 4. Access the "Problems" view in Eclipse. You should see two warnings of the type "Checkstyle Problem". 43 | 44 | Once everything works as described above, try the following: 45 | 46 | 1. Fix the code to make the Checkstyle warnings go away. To see the full list of coding rules checked by the tool, right-click on the project and select `Properties | Checkstyle | Configure` and play around with the viewer. 47 | 2. Change line 37 of file `AlternatingLabelProvider.java` to return `aLabel1` instead of `aLabel2`, and re-run the test. The test should fail. 48 | 3. Learn some [seriously useful shortcuts](http://www.vogella.com/tutorials/EclipseShortcuts/article.html). My personal favorites are: `CTRL-1`, `CTRL-SHIFT-R`, `CTRL-SPACE`, `CTRL-O`, `ALT-SHIFT-R`. 49 | 50 | --- 51 | 52 | Creative Commons License 53 | 54 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 55 | 56 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/Module-04.md: -------------------------------------------------------------------------------- 1 | # Module 4 - Unit Testing 2 | 3 | ## Description 4 | 5 | How can we have confidence that our code is working properly? Every time we write or change a line of code, we could be introducing a lethal bug. *Unit testing* is a practice wherein we automatically execute our code to check that it does what we think it should. With unit testing, we can build a possibly large collection of tests that can quickly be run, for instance every time we change the code, to make sure we didn't break anything that used to work. In this module, I will introduce mechanisms that facilitate unit testing (reflection and type annotations) and provide you with basic techniques for designing unit tests and evaluating their quality. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Be able to explain the foundational concepts of testing using the proper terminology; 12 | * Understand type annotations and program reflection and be able to use them effectively; 13 | * Be able to write unit tests with JUnit; 14 | * Be able to approach more advanced testing problems requiring reflection or mock objects; 15 | * Be able to understand the output of a test coverage tool such as EclEmma; 16 | * Be able to understand basic test suite adequacy criteria and the relations between them; 17 | 18 | ## Notes 19 | 20 | ### General Concepts and Definitions 21 | 22 | Software quality problems are often caused by programmers writing code that doesn't quite do what they expect. For example, this [bug](https://github.com/prmr/JetUML/issues/188) in JetUML made it impossible to see the directory structure in a file chooser object because of an incorrect *compound* (i.e., multi-part) condition. 23 | 24 | One way to detect bugs, and to gain confidence that a part of a program does what we expect, is to *test* it. [Testing](https://en.wikipedia.org/wiki/Software_testing) in the general sense is a software quality assurance technique that can take many forms. In the context of an introduction to software design, I will be using a specific testing approach called [Unit testing](https://en.wikipedia.org/wiki/Unit_testing). The idea of unit testing is to test a very small part of the program in isolation. This way, if the test fails, it is easy to know where to look for problems. 25 | 26 | In practice, a **unit test** consists in one execution of a **unit under test (UUT)** with some **input data** and the comparison of the result of the execution against some **oracle**. For example, the statement: 27 | 28 | ```java 29 | Math.abs(5) == 5; 30 | ``` 31 | 32 | technically qualifies as a test. Here the UUT is the library method `Math.abs(int)`, the input data is the integer 5, and the oracle is, in this case, also the value 5. When testing non-static method, it's important to remember that the input data includes the receiver object (the object that receives the method call). 33 | 34 | Although it is possible to test a system manually, in practice unit testing is done automatically. Since in software development the way to automate anything is to write a program to do it, to automate software testing we also write code to test other code. 35 | 36 | This task is typically supported by a **unit testing framework** like [xUnit](https://en.wikipedia.org/wiki/XUnit), which in the case of Java means [JUnit](https://en.wikipedia.org/wiki/JUnit). JUnit automates a lot of the mundane parts of unit testing, including collecting tests, running them, and reporting the results. 37 | 38 | ### Basics of JUnit 39 | 40 | In JUnit a unit test maps to a method. The code below illustrates a very simple unit test with JUnit. 41 | 42 | ```java 43 | public class AbsTest 44 | { 45 | @Test 46 | public void testAbsPositive() 47 | { 48 | assertEquals(5,Math.abs(5)); 49 | } 50 | 51 | @Test 52 | public void testAbsNegative() 53 | { 54 | assertEquals(5,Math.abs(-5)); 55 | } 56 | 57 | @Test 58 | public void testAbsMax() 59 | { 60 | assertEquals(Integer.MAX_VALUE,Math.abs(Integer.MIN_VALUE)); 61 | } 62 | } 63 | ``` 64 | 65 | The `@Test` [Annotation](https://docs.oracle.com/javase/tutorial/java/annotations/index.html) indicates that the method should be run as a unit test. This annotation is defined in the JUnit library, which must be added to a project's classpath before it is visible. The test method should typically contain at least one call to a UUT. The way to automatically verify that the execution of a UUT has the expected effect is to execute various calls to `assert*` methods. Assert methods are different from the `assert` statement in Java. They are declared as static methods of the class `org.junit.Assert*` and all they do is basically verify a predicate and, if the predicate is not true, report a *test failure*. The JUnit framework includes a GUI component called a *test runner*. To execute the JUnit test running from within Eclipse, right-click on a project that contains at least one JUnit test method, and select `Run As | JUnit Test`. When executing, all the JUnit test runner does is inspect the program, collect all methods flagged as unit tests using annotations, invoke them, then report whether the tests passed or failed. 66 | 67 | For additional instructions on how to use JUnit, read [this tutorial](http://www.vogella.com/tutorials/JUnit/article.html). To fully understand how JUnit works it is necessary to first read the [Annotations](https://docs.oracle.com/javase/tutorial/java/annotations/index.html) and [Reflection](https://docs.oracle.com/javase/tutorial/reflect/) tutorials. 68 | 69 | ### Test Suite Organization 70 | 71 | A collection of tests for a project is known as a **test suite**. A common question is how to organize our tests in a sensible manner. There are different approaches, but in Java a common way to organize tests is to have one *test class* per *project class*, where the test class collects all the tests that test methods of that class. Furthermore, it is common practice to locate all the testing code in a different *source folder* with a package structure that *mirrors the project's package structure*. The rationale for this idiom is that in Java classes in the same package are in the same *package scope* independently of their location in a file system. This means that classes and methods in the test package can refer to non-public (but non-private) members of classes the project code. This strategy simplifies the test code. The figure below illustrates this idea. 72 | 73 | ![Test Suite Organization](figures/m04-TestSuiteOrganization.png) 74 | 75 | ### Test Fixtures 76 | 77 | In test classes that group multiple test methods, it will often be convenient to define a number of "default" objects or values to be used as receiver objects and/or oracles. This practice will avoid the duplication of setup code in each test method. Baseline objects used for testing are often referred to as a *test fixture*, and declared as fields of a test class. However, when designing a test suite with JUnit, is it extremely important to know that JUnit *provides no ordering guarantee of any kind* for the execution of unit tests. In consequence, unit tests must be independent of each other so that they can be executed in any order. This further implies that no test method should rely on the fixture being left in a given state by another test. In most cases this precludes the use of the class constructor to initialize the fixture, because the constructor is only called once. The workaround is to nominate a method of the test class to execute before any test method. In JUnit this method is nominated using te `@Before` annotation. Fixture initialization code should therefore be located in this method. The use of test fixtures in JUnit is illustrated by most test classes in both sample projects. For example, in the Solitaire application's [TestSuitStackManager](https://github.com/prmr/Solitaire/blob/master/test/ca/mcgill/cs/stg/solitaire/model/TestSuitStackManager.java) class, the fixture consists of a single field `aSuitStackManager` which is initialized with a fresh object in method `setup`: 78 | 79 | ```java 80 | @Before 81 | public void setup() 82 | { 83 | aSuitStackManager = new SuitStackManager(); 84 | } 85 | ``` 86 | 87 | which is executed before the execution of every `@Test`-annotated method. 88 | 89 | ### Tests and Exceptional Conditions 90 | 91 | An important point when writing unit tests is that what we are testing is *that the UUT does what it's supposed to*. This means that when using design by contract, it does not make sense to test code with input that does not respect the UUT's preconditions, because the resulting behavior is *unspecified*. For example, for method [peek](https://github.com/prmr/Solitaire/blob/master/src/ca/mcgill/cs/stg/solitaire/model/SuitStackManager.java#L104), which has as precondition `assert !aStacks.get(pIndex).isEmpty();`, it would be awkward to try to write a test for an empty stack. What would be the oracle? 92 | 93 | The situation is very different, however, when exceptional behavior *is explicitly part of the interface*. For instance, for method [peek](https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html#peek--) of the Java `Stack` class, peeking an empty stack *should* result in an `EmptyStackException`. If it does not, then the `peek` method does not do what is expected, and this means it is faulty. 94 | 95 | This situation raises the question of how to test for exceptional conditions. In JUnit there are two idioms. One is to use the `expected` property of the `@Test` annotation: 96 | 97 | ```java 98 | @Test(expected = EmptyStackException.class) 99 | public void testPeekEmpty1() 100 | { 101 | Stack stack = new Stack<>(); 102 | stack.peek(); 103 | } 104 | ``` 105 | 106 | With this feature, JUnit will automatically *fail the test* if the execution of the corresponding test method completes *without* raising an exception of the specified type. This testing idiom is very useful, but limited in the sense that the exceptional behavior must be the last thing to happen in the test. In cases where it is desirable to execute additional testing code after testing the exceptional behavior, the following idiom can be used: 107 | 108 | ```java 109 | @Test 110 | public void testPeekEmpty2() 111 | { 112 | Stack stack = new Stack<>(); 113 | try 114 | { 115 | stack.peek(); 116 | fail(); 117 | } 118 | catch(EmptyStackException e ) 119 | {} 120 | } 121 | ``` 122 | 123 | This idiom is a bit convoluted, but does exactly what we want. If the UUT (the `peek` method here) is faulty in the sense that it does not raise an exception when it should, the program will keep executing normally and reach the following statement, which will force a test failure. If the UUT is (at least partially) correct in that it does raise the exception when it should, control-flow will immediately jump to the catch clause, thereby skipping the `fail();` statement. It is then possible to add additional code below the catch clause. 124 | 125 | ### Encapsulation and Unit Testing 126 | 127 | A common question when writing tests is, how can we test private methods? There are different possible avenues for answering that question, but experts disagree about which one is the right one: 128 | 129 | * Private methods are internal elements of other, accessible methods, and therefore are not really "units" that should be tested. Following this logic, the code in private methods should be tested indirectly through the execution of the accessible methods that call them; 130 | * The `private` access modifier is a tool to help us structure the project code, and tests can ignore it. 131 | 132 | Although I understand the rationale of the first approach, I personally fall in the second camp. There are many cases where a neat little method can be restricted to a class's scope, but it would still be valuable to test it in isolation. This situation is exemplified by the tests added to test [this fix](https://github.com/prmr/JetUML/issues/188). 133 | 134 | To by-pass the `private` access modifier, it is necessary to use [metaprogramming](https://docs.oracle.com/javase/tutorial/reflect/). 135 | 136 | The following code sample illustrates the main steps: 137 | 138 | ```java 139 | public class TestEditorFrame 140 | { 141 | private Method aCreateFileFilter; 142 | private EditorFrame aEditorFrame; 143 | 144 | @Before 145 | public void setup() throws Exception 146 | { 147 | aCreateFileFilter = EditorFrame.class.getDeclaredMethod("createFileFilter", String.class); 148 | aCreateFileFilter.setAccessible(true); 149 | aEditorFrame = new EditorFrame(UMLEditor.class); 150 | } 151 | ``` 152 | 153 | This test class definition includes, as part of its fixture, a field that stores a reference to an instance of class `Method` that represents the private method we want to test. The field is initialized in the `setup` method, and made accessible outside the class scope using the `setAccessible` method. This last part is what bypasses the `private` keyword. 154 | 155 | In the test class, we can then define a *convenience method* that launches the execution of the UUT (`createFileFilter`): 156 | 157 | ```java 158 | private FileFilter createFileFilter(String pFormat) 159 | { 160 | try 161 | { 162 | return (FileFilter) aCreateFileFilter.invoke(aEditorFrame, pFormat); 163 | } 164 | catch(InvocationTargetException | IllegalAccessException pException) 165 | { 166 | TestCase.fail(); 167 | return null; 168 | } 169 | } 170 | ``` 171 | 172 | In the methods of the test class, calling `createFileFilter` now has the same effect as calling `createFileFilter` on an instance of `EditorFrame`. This way the rest of the test looks pretty normal: 173 | 174 | ```java 175 | @Test 176 | public void testCreateFileFilteAcceptDirectory() 177 | { 178 | FileFilter filter = createFileFilter("PNG"); 179 | File temp = new File("foo"); 180 | temp.mkdir(); 181 | assertTrue(temp.isDirectory()); 182 | assertTrue(filter.accept(temp)); 183 | temp.delete(); 184 | } 185 | ``` 186 | 187 | except that it does not directly call the UUT, but a wrapper that uses metaprogramming to call the UUT while by-passing the access restriction of the `private` keyword. 188 | 189 | ### Testing with Stubs 190 | 191 | The key to unit testing is to test small parts of the program *in isolation*. But what happens if the part we want to test triggers the execution of a large chunk of the program? This situation is illustrated in the following design, which is a simplified version of the Solitaire sample application. The `GameEngine` has an `automove()` method that triggers the computation of the next move by dynamically delegating the task to a strategy, which could be any of the three options. Here we would like to write a unit test for the `GameEngine.automove()` method. 192 | 193 | ![Test Suite Organization](figures/m04-Stubs.png) 194 | 195 | In this task we face at least three problems: 196 | 197 | * Calling the `void executeMove(...)` method on any strategy will trigger the execution of presumably complex behavior by the strategy, which possibly depends on many other parts of the code. This does not align well with the concept of unit testing, where we want to test small pieces of code in isolation. 198 | * How can we know which strategy would be used by the game engine? Presumably we need to determine an oracle for the results. 199 | * How is this different from testing the strategies individually? 200 | 201 | The way out of this conundrum is the realization that the responsibility of `GameEngine.automove()` is *not* to compute the next move, but rather *to delegate this to a strategy*. So, to write a unit test that tests that the UUT does what it is expected, we *only need to verify that it relays the automove computation to a strategy*. This can be achieved with the writing of a *stub* (a.k.a. [mock object](https://en.wikipedia.org/wiki/Mock_object), although there's no agreement on terminology). 202 | 203 | An object stub is a greatly simplified version of an object that mimics its behavior sufficiently to support the testing of a UUT that uses this object. Using stubs is heavily dependent on types and polymorphism (see Module 2). Continuing with our `automove` situation, here we simply want to test that the method calls a strategy, so we will define a dummy strategy in the test method: 204 | 205 | ```java 206 | public class TestGameEngine 207 | { 208 | @Test 209 | public void testAutoPlay() throws Exception 210 | { 211 | class StubStrategy implements PlayingStrategy 212 | { 213 | private boolean aExecuted = false; 214 | 215 | public boolean hasExecuted() { return aExecuted; } 216 | 217 | @Override 218 | public void executeMove(GameEngine pGameEngine) 219 | { 220 | aExecuted = true; 221 | } 222 | } 223 | ``` 224 | 225 | This strategy does nothing except remember that its `executeMove` method has been called. We can then use an instance of this stub instead of a "real" strategy in the rest of the test. To inject the stub in the game engine, we again rely on metaprogramming: 226 | 227 | ```java 228 | @Test 229 | public void testAutoPlay() throws Exception 230 | { 231 | ... 232 | Field strategyField = GameEngine.class.getDeclaredField("aStrategy"); 233 | strategyField.setAccessible(true); 234 | StubStrategy strategy = new StubStrategy(); 235 | GameEngine engine = GameEngine.instance(); 236 | strategyField.set(engine, strategy); 237 | ``` 238 | 239 | at which point completing the test is simply a matter of calling the UUT (`automove`) and verifying that it did properly call the strategy: 240 | 241 | ```java 242 | engine.autoMove(); 243 | assertTrue(strategy.hasExecuted()); 244 | ``` 245 | 246 | The use of mock objects in unit testing can get extremely sophisticated, and frameworks exist to support this task (e.g., [jMock](http://www.jmock.org/)). In this textbook, we only cover the use of stubs/mocks for simple situations like the one illustrated here. Note that the situation illustrated here is boiled down to its essence for pedagogical purposes. In a realistic development scenario it would probably be judged overkill to use a stub to test a single method call. 247 | 248 | ### Selecting Test Inputs 249 | 250 | Up to now this module covered how to define an and implement unit tests from a practical standpoint, but avoided the question of *what to test*, or what inputs to provide to the UUT. It should be clear that for the vast majority of UUTs it is not physically possible to *exhaustively test* the input space. Consider this function which should return the roots of a quadratic formula: 251 | 252 | ```java 253 | /** 254 | * Finds the roots for ax^2 + bx + c 255 | */ 256 | public static double[] roots(double a, double b, double c) 257 | ``` 258 | 259 | The input space for this function is enormous. The much smaller input space induced by the same parameters of type `int` still comprises `2^(32+32+32)` distinct values. Assuming you can run one billion tests per second, testing all possible inputs on this simplified function would take (about) 182 times the age of the universe. Clearly we need to select some input, a problem known as **test case selection**, where a **test case** can be considered to be an set of input for an assumed UUT. For example, `(0,0,0)` is a test case for the `roots` function. The basic challenge of the test case selection problem is to test *effectively*, meaning to find a minimal set of test cases that provides us a maximal amount of "testing" for our function. Unfortunately, while it is obvious what a minimal number of test cases is, there is no natural or even agreed-upon definition of what an "amount of testing" is. 260 | 261 | There are two basic ways to approach the selection of test cases: 262 | 263 | * **Functional (or black-box) testing** tries to cover as much of the *specified* behavior of a UUT as possible, based on some external specification of what the UUT should do. For the `roots` function, this would mean, for example, ensuring we find inputs for formulas that have zero, one, two roots, some combination of positive and negative roots, etc. There are many advantages to black-box testing, including that you don't need access to the code of the UUT, testing can reveal problems with the specification, and that the tests can reveal missing logic. 264 | 265 | * **Structural (or white-box) testing** tries to cover as much of the *implemented* behavior of the UUT as possible, based on an analysis of the source code of the UUT. An example for the `roots` function is provided below. The main advantage of white-box testing is that it can reveal problems caused by low-level implementation details that are invisible at the level of the specification. 266 | 267 | Coverage of functional testing techniques is outside of the scope of this book, so the reminder of this module provides a brief introduction to structural testing. 268 | 269 | Consider the full implementation of the `roots` function: 270 | 271 | ```java 272 | 1 public static double[] roots(double a, double b, double c) 273 | 2 { 274 | 3 double q = b*b - 4*a*c; 275 | 4 if( q > 0 && a != 0 ) // Two roots 276 | 5 { 277 | 6 return new double[]{(-b+q)/2*a, (-b-q)/2*a}; 278 | 7 } 279 | 8 else if( q == 0 ) // One root 280 | 9 { 281 | 10 return new double[]{-b/2*a}; 282 | 11 } 283 | 12 else // No root 284 | 13 { 285 | 14 return new double[0]; 286 | 15 } 287 | 16 } 288 | ``` 289 | 290 | Here we can intuitively see that the code structure has three "natural" pieces that correspond to the three branches of the `if-else` statement. According to the principles of structural testing we would want to find test cases that at least execute these three pieces. For example: `(3,4,1),(0,0,1),(3,2,1)`. 291 | 292 | To support formal reasoning about code structure, we rely on the concept of a **control-flow graph (CFG)**. A CFG is a model of a UUT that represents all possible paths of execution through the CFG. Nodes in the graph correspond to either **basic blocks** or **branching statements**. Basic blocks are sequences of statements with a single entry and exit point (they are always executed together). Edges in the graph represent possible **control flow**, that is, the possibility that the program execution proceeds from one node to another. The figure below shows the CFG for the roots function. In this diagram circles represent start and end nodes, boxes represent basic blocks, and diamonds represent branching statements, with the control flow for both true (solid) and false (dashed) conditions. 293 | 294 | ![CFG Example](figures/m04-CFG.png) 295 | 296 | ### Test Adequacy Criteria 297 | 298 | We can now return to the problem of selecting test cases so that our test suite is "good enough". In structural testing, to know whether a test suite is "good enough" we rely on **test suite adequacy criteria** defined using the CFG. A *test suite adequacy criterion* is a predicate that is true or false for a pair (program, test suite). For example, a development team could decide that for a test suite to be good enough for their program, "80% of all statements should be executed when the test suite is executed". 299 | 300 | **Statement coverage** is the simplest criterion. It is defined as `(number of statements executed)/(number of statements in the program)`. In the example above, the test case `(3,4,1)` achieves `3/6 = 50%` statement coverage (here the start and end nodes are ignored). The rational for achieving statement coverage is simply that if a defect is present in a statement, it can't be detected if the statement is not executed. Full statement coverage corresponds to going through all the nodes in a CFG during the execution of a test suite. Note that line coverage and basic block coverage are alternatives that are practically equivalent to statement coverage. 301 | 302 | **Branch coverage** is defined as `(number of branches executed)/(number of branches in the program)`. In the example above, the test case `(3,4,1)` achieves `1/4 = 25%` branch coverage. Full branch coverage corresponds to going through all the edges in a CFG during the execution of a test suite. 303 | 304 | **Path coverage** is defined as `(number of paths executed)/(number of paths in the program)`. Path coverage corresponds to going through all the possible paths in a CFG during the execution of a test suite. In the example above, the test case `(3,4,1)` achieves `1/3 = 33%` path coverage. Path coverage is considered a *theoretical* criterion because although it can be a useful concept to reason about testing in general, it cannot be achieved in practice, in particular for code that contains loops. 305 | 306 | Because many defects can lurk in conditional statements, a number of coverage criteria target condition coverage specifically. In the case of condition-related coverage, is is more common to refer to coverage in binary terms (full coverage is achieved vs. not), as opposed to a fraction of the possible coverage. 307 | 308 | **Basic conditions coverage** is defined as "each basic condition must have a True and a False outcome at least once during the execution of the test suite". In the example above, the test case `(3,4,1)` does not achieve basic condition coverage because of the six possible condition-outcome pairs, only `true(q > 0)` on line 4 and `true(a!=0)` on line 4 are covered. 309 | 310 | **Branch and conditions coverage** is defined as satisfying both the branch and basic conditions criteria. In the example above, the test case `(3,4,1)` does not achieve branch and condition coverage because it achieves neither the branch nor the basic condition criteria. 311 | 312 | **Compound conditions** is defined as achieving each possible evaluation of compound conditions. In the general case the compound conditions criterion is also considered theoretical because of the the large number of resulting combinations. 313 | 314 | Some of the coverage criteria have a **subsumption relation** between them. For criterion A to *subsume* criterion B means that if A is achieved, B is implicitly achieved. The following subsumption relations exist between the criteria seen in this module (the relation is [transitive](https://en.wikipedia.org/wiki/Transitive_relation)): 315 | 316 | * Branch subsumes Statement 317 | * Path subsumes Branch 318 | * Branch and conditions subsumes both branch and basic conditions (by definition) 319 | * Compound conditions subsumes branch and conditions 320 | 321 | ### Practical Coverage Analysis 322 | 323 | Test coverage is typically computed by tools that *instrument* the source or bytecode and report how much coverage is achieved for different criteria supported by the tool. For example, EclEmma supports instruction, line (a proxy for statement), and branch coverage. See the Module 0 exercises for instructions on how to install and run EclEmma. 324 | 325 | ### Acknowledgements 326 | 327 | The part of this module on test case selection and structural testing was adapted from a lecture originally created by Prof. Andreas Zeller based on material from the book "Software Testing and Analysis: Process, Principles, and Techniques", by Pezze & Young, Wiley, 2008 328 | 329 | ## Reading 330 | 331 | * The [Java Tutorial - Annotations](https://docs.oracle.com/javase/tutorial/java/annotations/index.html) 332 | * The [Java Tutorial - Reflection](https://docs.oracle.com/javase/tutorial/reflect/) 333 | * The [Vogella Unit Testing Tutorial](http://www.vogella.com/tutorials/JUnit/article.html) 334 | * Solitaire v0.3 [TestGameModel](https://github.com/prmr/Solitaire/blob/v0.3/test/ca/mcgill/cs/stg/solitaire/model/TestGameModel.java) as a sample unit test demonstrating the use of reflection and simple mock objects. 335 | 336 | ## Exercises 337 | 338 | Exercises prefixed with **(+)** are optional, more challenging questions aimed to provide you with additional design and programming experience. Exercises prefixed with **(P)** (for "project") will incrementally guide you towards the ultimate completion of a complete Solitaire application. 339 | 340 | The best way to practice unit testing is, unsurprisingly, to write tests for as much code as possible. The exercises below provide some suggestions, but for additional practice you can write tests for practically any available code. 341 | 342 | For maximum learning effectiveness, I recommend peeking at the [answers](answers/Answers-04.md) only after giving the problems an honest try. 343 | 344 | 1. Write unit tests for the Java `Math.abs()` and `Math.min(...)`. 345 | 346 | 2. Write unit tests for the Java `Stack` methods `pop`, `push`, and `peek`. 347 | 348 | 3. Create CFGs for all the methods of class [RecentFilesQueue](https://github.com/prmr/JetUML/blob/v1.0/src/ca/mcgill/cs/stg/jetuml/framework/RecentFilesQueue.java), except for `serialize` and `deserialize`. With the help of EclEmma, write tests that achieve branch coverage. Manually compute the coverage for all three condition criteria. 349 | 350 | 4. :star: Extend the previous exercise to include the `serialize` and `deserialize` methods. 351 | 352 | 5. :spades: Write unit tests for all the classes of the Solitaire application you have developed so far. Compute the coverage of your test suite with EclEmma. 353 | 354 | 6. :star: Run EclEmma on JetUML and identify some untested code. Write a new unit test and submit it as a pull request. 355 | 356 | --- 357 | 358 | Creative Commons License 359 | 360 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 361 | 362 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/Module-06.md: -------------------------------------------------------------------------------- 1 | ## Module 6 - Inversion of Control 2 | 3 | ## Description 4 | 5 | The idea of inversion of control is one of the most powerful intellectual tools in a software designer tool-box. It allows us to build incredibly sophisticated applications while keeping the overall design complexity down to a manageable level. The Observer pattern is extremely common in software development, and it's realized by most GUI toolkits on most software development platforms, from desktop to web to mobile applications. This module is dedicated to the principle of inversion of control (IoC) and its realization in the Observer pattern, also called the Model-View Controller (MVC) pattern. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Be able to use the Observer design pattern effectively; 12 | * Be able to design and implement simple graphical user interfaces with JavaFX; 13 | * Understand the concept of an application framework; 14 | 15 | ## Notes 16 | 17 | ### Motivation 18 | 19 | One of the main problems that motivates inversion of control in design is situations where a number of objects need to be kept consistent with a certain state. An example from the programming domain itself is an integrated development environment like Eclipse, which presents different views of the code. For example, the Outline View shows the outline of a class that can also be viewed in the text editor, etc. 20 | I illustrate a simpler instance of this problem with the [LuckyNumber](https://github.com/prmr/SoftwareDesignCode/blob/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) toy application. When launched this application shows a number between 1 and 10 in three different ways (or with three different *views*): 21 | 22 | ![](figures/m06-luckyNumber.png) 23 | 24 | Each view of the number also allows the user to change the number, and the change is immediately reflected in all views. 25 | 26 | A naive (and inferior) way to implement this functionality is through *complete pairwise dependencies*. 27 | 28 | ![](figures/m06-dependencies.png) 29 | 30 | This design suffers from (at least) the following three inter-related limitations: 31 | 32 | * **High coupling**: Each panel explicitly depends on many other panels. 33 | * **Complexity**: Complex idiosyncratic program logic is required to keep the different panels consistent. 34 | * **Low Extensibility**: To add or remove a panel, it is necessary to modify all other panels. 35 | 36 | Furthermore, these limitations all increase quadratically in the number of panels, given that there are `n*(n-1)` directed edges in a complete graph with `n` vertices. 37 | 38 | The way out of this nauseating approach is to separate program elements responsible for *storing state* from program elements responsible for *viewing state*, from program elements responsible for *changing state*, and to use various mechanisms to achieve loose coupling between these. This profoundly influential idea is commonly known as **the Observer Design Pattern** or (somewhat alternatively) **the Model-View-Controller** architecture (MVC). In this book I stick to the "Observer pattern" terminology, but it's good to be aware that in other contexts people may talk about the MVC and refer to essentially the same thing. 39 | 40 | ### The Observer Design Pattern 41 | 42 | The central idea of the Observer design pattern is to store state of interest in specialized objects, and to allow other objects to *observe* this state. The class diagram below illustrates how this is realized for the LuckyNumber application. 43 | 44 | ![](figures/m06-basicObserver.png) 45 | 46 | In this situation, the object in charge of keeping state is an instance of `Model`. The `Model` class in the Observer design pattern can alternately be called "Subject", or even "Observable". Here an instance of `Model` simply keeps track of an integer and allows clients to query and mutate this integer. Where things become interesting is that the `Model` class also includes an aggregation to an `Observer` interface, with methods to add and remove `Observer` instances from its collection. This is also called *(de)registering* observers. Classes that define objects that would be interested in observing the state of the model must then declare to implement the `Observer` interface. Through polymorphism, we thus achieve loose coupling between the model and its observers. Specifically: 47 | 48 | * The model can be used without any observer; 49 | * The model is aware that it can be observed, but its implementation does not depend on any concrete observer class. 50 | * It is possible to register and de-register observers at run-time. 51 | 52 | Two key questions about the relation between a model an its observers are: 53 | 54 | * How do the observers learn that there is new information in the model that they need to know about? 55 | * How do they access this information? 56 | 57 | The answer to the first question is that whenever the model determines that there is a change in the model worth reporting to observers, it cycles through the observers and calls a certain method on them. This method has to be defined on the `Observer` interface and is usually called a "callback" because of the inversion of control that it represents. We talk of inversion of control because to find out information from the model the observers do not call a method on the model, they instead "wait" for the model to call them (back). This is often referred to as the "Hollywood Principle" ("don't call us, we'll call you"). That is also why the method that is called by the model on the observer is called a "callback". The following sequence diagram illustrates what happens when we change the model on the "LuckyNumber" application. 58 | 59 | ![](figures/m06-callbacks1.png) 60 | 61 | Here, inside the state-changing method `setNumber(int)`, we added logic to loop through each observer and call the method `newNumber` on each. This `newNumber` method is a "callback" that the observers will expect to be called every time there is a state change in the model. The implementation of the callback dictates how each observer reacts to the change in state. 62 | 63 | Another way to think about callback methods is as "events", with the model being the "event source". With this paradigm, the model generates a series of "events" that correspond to different state changes, and other objects are in charge of reacting to these events. What events correspond to in practice is simply methods calls. 64 | 65 | The figure below provides a summary of the main roles of the Observer pattern and their relation. 66 | 67 | ![](figures/m06-ObserverSummary.png) 68 | 69 | Variation points for the Observer design pattern include: 70 | 71 | * Whether to make the `notifyObserver` methods public or private. If public, clients with references to the model get to control when notifications are issued. If private, it is assumed that the method is called at appropriate places in the state-changing methods of the model. 72 | * What callbacks methods to define on an abstract observer. An abstract observer can have any number of callbacks that can correspond to different types of events. 73 | * What data flow strategy to use to move data between the model and observers (push, pull, none, or both, as appropriate). 74 | * Whether to use a single abstract observer or multiple ones. Multiple abstract observers with different combinations of callbacks give clients more flexibility to respond to certain events or not. 75 | * How to couple observers with the model if observers need to query or control the model. Here the use of the interface segregation principle is recommended. 76 | 77 | ### Observer Design Case Study 78 | 79 | Let's consider how we would approach the following question: 80 | 81 | > We are interested in an inventory system capable of keeping track of electronic equipment. An `Item` of equipment records a serial number (`int`) and production year (`int`). An `Inventory` object aggregates a bunch of `Item`s. Clients can add or remove `Item`s from the `Inventory` at any time. Various entities are interested in changes to the state of the `Inventory`. For example, it should be possible to show the items in the `Inventory` in a `ListView`. It should also be possible to view a `PieChart` representing the proportion of `Item`s in the `Inventory` for each production year (e.g., 2004=25%; 2005=30%, etc.). Views should be updated whenever items are added or removed from the Inventory. 82 | 83 | As a first take on this problem we can model the basic elements of the domain with a class diagram: 84 | 85 | ![](figures/m06-cs1.png) 86 | 87 | This diagram captures all the relevant domain elements but, little else. The following step is to instantiate a basic Observer pattern. At this stage this simply requires realizing which domain object plays which role. Here the object containing the observable state would be instances of `Inventory`, and the objects observing this state would be `PieChart` and `ListView`. 88 | 89 | ![](figures/m06-cs2.png) 90 | 91 | Now the basic mechanism for linking observers with the `Inventory` is visible, but some of the interesting questions are left unanswered. At this point we need to design callback methods. Since there are only two possible events in this simple problem (adding and removing items), there are two choices for callbacks: 92 | 93 | * A single callback that indicates that an item was added or removed; 94 | * One callback for items added and one for items removed. 95 | 96 | Here the first option would end up being called something like `itemAddedOrRemoved` and concrete observer would have to check a boolean flag to determine what happened. This option clearly has the smell of being not quite right. Indeed the second one is the more elegant choice. The next question is how to tell observers which item has been added or removed. Using the pull strategy , we could include a reference to the `Inventory` that changed as part of the callback, and add a method `getLastItemAddedOrRemoved()`: 97 | 98 | ![](figures/m06-cs3.png) 99 | 100 | Although technically workable, this solution both looks and is clumsy. A much more natural option here seems to be to simply pass the added/removed item to the callback, to have something like `itemAdded(Item)`. Another somewhat more radical option, is to make the `Inventory` `Iterable` and to expect the observers to refresh themselves completely every time they receive an event. In some cases this make sense, but here let's stick to the use of the push strategy. 101 | 102 | The last question is how to trigger notifications. Here we'll choose to add a private `notifyObservers` method that is automatically called whenever `addItem` or `removeItem` is called. 103 | 104 | The current class diagram is thus: 105 | 106 | ![](figures/m06-cs4.png) 107 | 108 | Assuming two observers (one of each type) are registered with the inventory, a sequence that illustrates an item being added is thus: 109 | 110 | ![](figures/m06-cs5.png) 111 | 112 | Note the following details: 113 | 114 | * The `notifyObservers` method takes in a parameter to facilitate its implementation. 115 | * The names of the methods on the `Observable` describe *commands* such as `addItem`, whereas the names of the call back describe the corresponding *result of the command as an event in the past*, such as `itemAdded`. The use of effective names greatly contributes to the usability of the Observer pattern. 116 | 117 | Finally, let's assume that after using this design in a version of the system, a new type of observer is added, called the `TransactionLogger`. This type of observers is only interested in items being added to the inventory. In the current design, the class would have to implement the `itemRemoved` callback to do nothing. In this case, we can improve the design to allow different types of observers to register to only the events they care about. However, note that this doubles the number of observer management methods on the model. 118 | 119 | ![](figures/m06-cs6.png) 120 | 121 | ### General Introduction to GUI Development with JavaFX 122 | 123 | The code that implements the Graphical User Interface (GUI) portion of an application makes heavy use of the Observer design 124 | pattern. This section of Module 6 is a brief introduction to GUI programming that serves the dual purpose of being a first approach to GUI frameworks and reinforcing your knowledge of the Observer pattern through its application in practice. This part 125 | of the module relies on [JavaFX](http://www.oracle.com/technetwork/java/javafx/overview/index.html), a modern and extensive GUI framework for the Java language. However, the general concepts presented here should apply to practically any GUI development framework. 126 | 127 | Conceptually the three essential components of a GUI application are: 128 | 129 | * **Component Graph:** The "actual" interface is comprised of a number of objects that represent both visible (e.g., buttons and invisible (e.g. regions) elements of the application. These objects are typically organized as a tree, with the root of the tree being the "top" (or main) window or area of the GUI. In modern GUI frameworks, constructing a component graph can be done by writing code, but also through configuration files generated by GUI building tools. Ultimately the two are equivalent, because once the program runs the result is the same: a tree of plain Java objects that form the user interface. The design of the library classes that support the construction of a component graph makes heavy use of polymorphism, the Composite design pattern, and the Decorator design pattern. 130 | 131 | * **Framework Event Loop:** A GUI application does not starts the same way as a "plain" program (i.e., through a `main` method explicitly passed as argument to the execution environment). Instead, the application is started by *launching* the GUI framework. The framework is then responsible for instantiating the component graph, and then starting an "event loop". The event loop is a functionality of the framework whereby the framework monitors events triggered by input devices (e.g., moving the mouse) and automatically maps these low-level events into specific interactions with components in the graph (e.g., mousing over a text box, clicking a button). 132 | 133 | * **Event Handling:** A GUI framework executes application-specific code by executing callback methods defined by the application developers. In this sense, GUI events are essentially a manifestation of state change notifications in the Observer design pattern. 134 | 135 | Given these three concepts, we can summarize that creating and executing a GUI application involves three basic steps: defining the component graph, defining event handlers and registering them with events of interests on objects in the component graph, and starting the framework. 136 | 137 | #### The Component Graph 138 | 139 | The component graph can be seen from four different perspectives (old image using Swing, to be updated eventually): 140 | 141 | ![](figures/m06-ComponentGraph.png) 142 | 143 | In JavaFX, the type hierarchy available to define component is extensive; the following is an incomplete simplification: 144 | 145 | ![](figures/m06-JavaFX.png) 146 | 147 | The following code from the [LuckyNumber sample](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) shows the 148 | construction of a component graph: 149 | 150 | ```java 151 | @Override 152 | public void start(Stage pPrimaryStage) 153 | { 154 | ... 155 | GridPane root = createPane(); // The root of the GUI component graph 156 | root.add(new SliderPanel(model), 0, 0, 1, 1); 157 | root.add(new IntegerPanel(model), 0, 1, 1, 1); 158 | root.add(new TextPanel(model), 0, 2, 1, 1); 159 | ... 160 | pPrimaryStage.setScene(new Scene(root)); 161 | ... 162 | } 163 | 164 | private static GridPane createPane() 165 | { 166 | GridPane root = new GridPane(); 167 | root.setHgap(GAP); 168 | root.setVgap(GAP); 169 | root.setPadding(new Insets(MARGIN)); 170 | return root; 171 | } 172 | ``` 173 | 174 | #### The Framework and Event Loop 175 | 176 | Frameworks differ from libraries in an essential way: they control the flow of the program and only call 177 | into application (i.e., developer-defined) code when they "decide". For this reason frameworks follow a paradigm called 178 | "inversion of control". Inversion of control is directly related to the observer pattern, since the framework determines 179 | when GUI elements (the subjects) trigger notification to their observers (the event handlers). 180 | 181 | ![](figures/m06-framework-a.png) 182 | 183 | When the framework runs, the following loops executes ad infinitum: 184 | 185 | ![](figures/m06-framework-b.png) 186 | 187 | The following code from the [LuckyNumber sample](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) shows how the framework is launched: 188 | 189 | ```java 190 | public static void main(String[] pArgs) 191 | { 192 | launch(pArgs); 193 | } 194 | 195 | @Override 196 | public void start(Stage pPrimaryStage) 197 | { 198 | pPrimaryStage.show(); 199 | } 200 | ``` 201 | 202 | Here the `main` method does not start executing the application code: instead, it simply calls the static method `Application.launch`. This method executes a number of background tasks and, when it is ready, it calls the `start` method (the `start` method is, in that sense, a callback). As shown above, the `start` method builds the component graph and, once that's done, it calls `show` on the object that represents the top-level GUI element, at which point the GUI application becomes visible and reactive. 203 | 204 | 205 | #### Event Handling 206 | 207 | In GUI frameworks objects in the component graph act as subjects in the observer pattern. For example, in the [LuckyNumber sample](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) the code: 208 | 209 | ```java 210 | aText.setOnAction(new EventHandler() 211 | { 212 | @Override 213 | public void handle(ActionEvent pEvent) 214 | { 215 | int lInteger = 1; 216 | try 217 | { 218 | lInteger = Integer.parseInt(aText.getText()); 219 | } 220 | catch(NumberFormatException pException ) 221 | { 222 | // Just ignore. We'll use 1 instead. 223 | } 224 | aModel.setNumber(lInteger); 225 | } 226 | }); 227 | ``` 228 | 229 | creates an instance of an anonymous class that is a subtype of `EventHandler` to handle the case where a user presses "Enter" when the cursor is on the text field. To register this handler with the source of the event, a reference to the 230 | instance is immediately provided as argument to the method `setOnAction` method of class `TextField`. This `setOnAction` method is the equivalent to `addObserver` in the observer design pattern. Note that it is possible to pass 231 | *any* instance of `EventHandler` to `setOnAction`. For example, another common design idiom is to define the class that contains the button (here `IntegerPanel`) to itself be the handler. For this to be possible, the class needs 232 | to implement `EventHandler` and provide a `handle` method. Once that's done, it's then possible to 233 | pass `this` to `setOnAction`. 234 | 235 | 236 | ## Reading 237 | 238 | * The [Module 6 code samples](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) 239 | * The [JavaFX Tutorial](http://docs.oracle.com/javafx/2/get_started/hello_world.htm) 240 | * Solitaire v0.3 The [DeckView](https://github.com/prmr/Solitaire/blob/v0.3/src/ca/mcgill/cs/stg/solitaire/gui/DeckView.java) class as an example of a GUI observer. 241 | 242 | ## Exercises 243 | 244 | Exercises prefixed with :star: are optional, more challenging questions aimed to provide you with additional design and programming experience. Exercises prefixed with :spades: will incrementally guide you towards the ultimate completion of a complete Solitaire application. 245 | 246 | For maximum learning effectiveness, I recommend peeking at the [answers](answers/Answers-06.md) only after giving the problems an honest try. 247 | 248 | 1. Extend the code of the [LuckyNumber](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) sample application to include a Roman Numerals panel. 249 | 2. Re-write the code of the [LuckyNumber](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) sample application so that data flows between 250 | the model and the observers using the pull strategy. Create a class diagram and a sequence diagram that model the key aspects of this solution. 251 | 3. Write the code that implements a skeleton application that corresponds to the Observer Design Case Study. Write a driver program that adds and removes item to make sure everything works as expected. 252 | 4. Study the code of the [Solitaire](https://github.com/prmr/Solitaire/tree/v0.4) application to see how the `GameModelListener` interface is used. Create a class diagram and a sequence 253 | diagram to document how the observer pattern is used in relation to the `GameModel` class. 254 | 5. Write a JavaFX application with a button and a label, which writes the current date and time in the label every time the button is pressed. You can obtain the current date and time with the following API call `LocalDateTime.now().toString();` 255 | 6. Re-write the code of the [LuckyNumber](https://github.com/prmr/SoftwareDesignCode/tree/master/module06/ca/mcgill/cs/swdesign/m6/LuckyNumber.java) sample application to add a "Notify" button that notifies observers of the value in the model. The value should only be propagated when the button is clicked. 256 | 7. Study the code of the [Solitaire](https://github.com/prmr/Solitaire/tree/v0.4) application to see how the `DiscardPileView` relies on the observer design pattern to respond to GUI events, and also updates to the model. Draw a class diagram and a sequence diagram to capture the information you discover. 257 | --- 258 | 259 | Creative Commons License 260 | 261 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 262 | 263 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/Module-08.md: -------------------------------------------------------------------------------- 1 | ## Module 8 - Design Patterns 2 | 3 | ## Description 4 | 5 | This module will explore design solutions that incorporate design patterns an inheritance and introduce the Visitor design pattern. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Be able to use the Visitor Design Pattern effectively; 12 | * Be able to determine when inheritance can be effectively used within different design patterns including the Visitor, Decorator, Composite, Command, and Strategy patterns. 13 | * Be able to correctly instantiate design patterns with inheritance. 14 | 15 | ## Notes 16 | 17 | ### Visitor Design Pattern 18 | 19 | ### Design Pattern Review Scenario 20 | 21 | We will explore how to combine design patterns by creating a design for a hypothetical mobile robotics system. In this system, a `Robot` class represents a three-wheeled robot with two active wheels and one free wheel that can also rotate around a pivot. The `Robot` class provides very basic control primitives through a method `void moveLeftWheel(double pRadians)` a similar method for moving the right wheel, and a similar method for moving both wheels in synch. The `pRadians` parameter specifies how much to turn the wheel, e.g., `pRadians=2*PI` turns the wheel a full circle. 22 | 23 | ![](figures/m08-robot.png) 24 | 25 | We want to expand this design to implement the following requirements: 26 | 27 | 1. It should be possible to define higher-level commands for the robot in terms of the basic primitives. For example it should be possible to define a "Forward Move" command that moves both wheels by the same amount. The number of different commands should be extensible; 28 | 2. Commands should be parameterizable, e.g., "move forward 1 meter" vs 2 meters, etc. 29 | 3. Any command should be reversible; 30 | 4. Commands should have a name that can be discoverable at run time. For example, a "Move forward 1 meter". 31 | 5. It should be possible to aggregate commands into more complex "macro commands". For example, a "Back and Forth" command could involve a forward move followed by a backward move. 32 | 6. It should be possible for components in the system other than the robot to be notified of commands issued on a `Robot` object. Three components interested in robot commands include a `CommandLogger` that prints all commands with a time stamp, `CommandRecorder` that can be issued a request to record commands (or to stop recording them), and to control the robot to replay the recording; a `RobotConsole` component that visually shows the path of the robot in a graphical user interface. 33 | 7. The system should remember the last command issued to a robot, and provide a convenience method `reexecute()` to re-execute this command. The reexecution of the command should be considered a new, separate command. 34 | 35 | In addition to these requirements, the final design should exhibit a number of qualities: 36 | * Effective code reuse: the design should limit code duplication; 37 | * Loose coupling: classes should not depend on interfaces they do no need; 38 | * Robustness: The likelihood of `NullPointerExceptions` should be, well, null. 39 | 40 | 41 | ## Reading 42 | 43 | * [A webpage on the Visitor design pattern](https://en.wikipedia.org/wiki/Visitor_pattern) 44 | 45 | ## Exercises 46 | 47 | For maximum learning effectiveness, I recommend peeking at the [answers](answers/Answers-08.md) only after giving the problems an honest try. 48 | 49 | 1. Add Visitor support to the class hierarchy below by completing the class diagram. Include a `PrintVisitor` as part of the design. Using a sequence diagram, show a scenario of a traversal through a directory object with the print visitor. The `PrintVisitor` prints the name of each file it visits. Implement a mock-up of the design in code, and use the debugger to validate your sequence diagram. 50 | ![](figures/m08-FileSystem.png) 51 | 2. Extend the file system class hierarchy to include a `HiddenDirectory` class that is a subclass of `Directory`. For the purpose of this exercise, a hidden directory behaves just like a directory, but prints the name as `"." + getName().` Adjust both the diagrams and the code in consequence. 52 | 3. Instead of using a subclass for `HiddenDirectory`, implements this feature using the Decorator Design pattern. Adjust both the diagrams and the code in consequence. 53 | 4. Implement a `DeleteVisitor` that find a file with a name passed as parameter to the visitor, and delete all its children, if the file is a directory or a symbolic link that refers to a directory. Once this works, try changing the code so that the specified `IFile` also gets deleted. Is this a good idea? 54 | 5. Run the [University Demo](artifacts/module-08/). What is the order of traversal implemented in `SearchVisitor`? What happens if two committees with the same parent node have the same name? Use the debugger to confirm your answer. 55 | 6. Experiment with different traversal orders for the visitor. 56 | 7. With the University example, implement a `CommitteeDepthVisitor` that can discover the sub-committee depth of a committee that matches an input query. For example, a root committee would have value 0, a sub-committee, 1, and a sub-sub-committee 3. 57 | 8. Solve the review question by producing a class diagram, sequence diagrams to illustrate a command execution, and implement a mock-up of your solution. 58 | 59 | --- 60 | 61 | Creative Commons License 62 | 63 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 64 | 65 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/Module-09.md: -------------------------------------------------------------------------------- 1 | ## Module 9 - Concurrency 2 | 3 | ## Description 4 | 5 | *Concurrent programming* is a paradigm that allows developers to write code that can execute in parallel (on multi-core systems) or with the illusion of parallelism (on single-core ones). Concurrent programming is very, very challenging and is ideally only used in support of well-defined and well-understood design problems, such as performing background operations or displaying animations. In fact, many typical concurrent programming problems already have a framework-based solution that hides the use of concurrency primitives. In this module, I will present the foundations of object-oriented concurrent programming and a few examples of the use of concurrency in practice. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Understand the concept of a Thread and its usefulness for programming; 12 | * Be able to write basic concurrent programs in Java; 13 | * Understand the causes of basic concurrency errors including race conditions and deadlocks, and the mechanisms that help prevent them. 14 | * Be able to recognize when to and when not to use concurrency in application design; 15 | 16 | ## Notes 17 | 18 | ## Reading 19 | 20 | * [Java Tutorial - Concurrency](https://docs.oracle.com/javase/tutorial/essential/concurrency/) 21 | 22 | ## Exercises 23 | 24 | Exercises prefixed with :star: are optional, more challenging questions aimed to provide you with additional design and programming experience. For maximum learning effectiveness, I recommend peeking at the [answers](answers/Answers-09.md) only after giving the problems an honest try. 25 | 26 | 1. Write a program where one thread (`NumberIncrementer`) keeps adding to a shared data structure (make it a class `NumberBox`), and another thread (`NumberPrinter`) sleeps, periodically wakes up, and prints whatever number is in the box. Do you need to use synchronization? why or why not? 27 | 2. Change the program of Exercise 2 so the number-incrementer threads only increments number every, say, 1 second, and the printer 28 | thread only obtains a number when a new one is available. Use conditions. 29 | 3. Enhance the version developed for Exercise 2 so that a third (timer) thread interrupts both counter and printer threads after the number reaches 10. 30 | 31 | --- 32 | 33 | Creative Commons License 34 | 35 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 36 | 37 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/Module-10.md: -------------------------------------------------------------------------------- 1 | ## Module 10 - Serialization 2 | 3 | ## Description 4 | 5 | Sometimes, the data in a running program needs to be transferred out of the program, for example to be stored in a file or transmitted over a network. In this module, I will cover the main techniques for serializing object graphs using a variety of frameworks including Java's binary serialization and JavaBeans frameworks and JSON serialization APIs. This module will also conclude the course with an introduction to the agile development practice called refactoring. 6 | 7 | ## Learning Objectives 8 | 9 | After this module you should: 10 | 11 | * Understand the basic problems and trade-offs related to the serialization of objects graphs; 12 | * Be able to make an informed decision about the best serialization strategy for a given design situation; 13 | * Be able to serialize simple object graphs in Java using JDK frameworks and a JSON API; 14 | * Know about the concepts of code smells and refactoring 15 | * Be able to recognize common bad smells in code; 16 | * Be able to perform a selection of common object-oriented refactorings; 17 | 18 | ## Notes 19 | 20 | ## Reading 21 | 22 | * Introduction to [the JSON format](http://json.org/); 23 | * Documentation for [XMLEncoders](http://www.oracle.com/technetwork/java/persistence4-140124.html); 24 | * JetUML v1.0 [PersistenceService](https://github.com/prmr/JetUML/blob/v1.0/src/ca/mcgill/cs/stg/jetuml/framework/PersistenceService.java) class as an example of XML serialization. 25 | * Chapter 3 and descriptions of refactorings seen in class in [Fowler's book](https://mycourses2.mcgill.ca/d2l/le/content/203788/viewContent/2563582/View) 26 | 27 | 28 | ## Exercises 29 | 30 | Exercises prefixed with :star: are optional, more challenging questions aimed to provide you with additional design and programming experience. 31 | 32 | 1. Serialize the `Corporation` object created in `labtest01.Driver` using the four types of serialization seen in class. Note that saving the graph in CSV will require some hand-crafted encoding conventions, such as repeating field values. Using the debugger, inspect the deserialized object graph to investigate whether the sharing of the identity of `Item` objects is respected or not. 33 | 2. Once you have a file with the serialized version of the corporation in all four formats, add a boolean field to the `Item` class, for example `aOnSale`. Then, attempt to deserialize the saved versions. What happens then? Solve the problem for each technique using the most appropriate mechanism. 34 | 3. On the Solitaire sample application, use the Eclipse Extract Interface refactoring tool on Deck and extract the methods size and draw. Call the new interface `CardSource` for example. Notice how the tool automatically figures out that the interface to a `Deck` instance can be narrowed down to a `CardSource` in `WorkingStackManager`. 35 | 36 | --- 37 | 38 | Creative Commons License 39 | 40 | Unless otherwise noted, the content of this repository is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 41 | 42 | Copyright Martin P. Robillard 2017 -------------------------------------------------------------------------------- /modules/answers/Answers-01.md: -------------------------------------------------------------------------------- 1 | # Module 1 - Answers 2 | 3 | Answers and answer sketches to the Module 1 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | The code can be found [here](EnumeratedCard.java). With the two helper methods to get the suit and rank of the card, the answer is pretty close to the original one. To have to calculate the rank and suit from indices has lower understandability and robustness than simply storing the information directly. However, the fact that the `Card` is an immutable enumerated type gives us a number of benefits, such as the certainty that there will never be duplicate cards in a program. Personally I would still favor the version seen in class. 8 | 9 | ## Exercise 2 10 | 11 | Notice how you can define the color enumerated type as an inner type of the `Suit` type, which makes sense, because this isn't any color, but the color of a card's suit. Note also that I could have had more compact code in `getColor` by working with the enumerated value's index, but that is brittle. The current code will work even if the order of enumerated values is changed. 12 | 13 | ``` 14 | public enum Suit 15 | { 16 | CLUBS, DIAMONDS, SPADES, HEARTS; 17 | 18 | enum Color 19 | { 20 | RED, BLACK; 21 | } 22 | 23 | public Color getColor() 24 | { 25 | if( this == CLUBS || this == SPADES ) 26 | { 27 | return Color.BLACK; 28 | } 29 | else 30 | { 31 | return Color.RED; 32 | } 33 | } 34 | 35 | public String toString() 36 | { 37 | return name().substring(0,1) + name().substring(1, name().length()).toLowerCase(); 38 | } 39 | } 40 | ``` 41 | 42 | ## Exercise 3 43 | 44 | **Sketch:** There are many different ways to answer this question. For a single Joker, it would make sense to have an additional boolean field `aIsJoker`. The interesting questions to solve become, what should the rank and suit of a Joker be, and what should `getRank` and `getSuit` return for a joker? Experiment with design by contract and exception handling. 45 | 46 | ## Exercise 4 47 | 48 | **Sketch:** An enumerated type seems like the right idea here to capture the type of joker. However, as I mentioned in class, it's not a good idea to consider null to be a legal value for an enumerated type, so I would recommend including an enum value to represent the case where the card is not a joker. The issues of what to do with rank and suit of jokers is the same as for Exercise 3. 49 | 50 | ## Exercise 5 51 | 52 | Assuming instances of class `Card` are immutable, it's ok to do the following, although we will see more elegant solutions in Module 2. 53 | 54 | ``` 55 | public List getCard() 56 | { 57 | return new ArrayList<>(aCards); 58 | } 59 | ``` 60 | 61 | ## Exercise 6 62 | 63 | The code of a partial solution can be found [here](MultiDeck.java). The solution leaves out the copying part of the assignment. To implement the object copying, you will need a way to copy `Deck` and `Card` instances. One way to do this at this point is simply to define copy constructors for these two classes. 64 | 65 | ## Exercise 7 66 | 67 | ![Answer to exercise 7](m01-a-multideck.png) 68 | 69 | ## Exercise 8 70 | 71 | The class is well encapsulated. The only part of the internal state returned is the inner `String`, but that is an immutable object. -------------------------------------------------------------------------------- /modules/answers/Answers-02.md: -------------------------------------------------------------------------------- 1 | # Module 2 - Answers 2 | 3 | Answers and answer sketches to the Module 2 practice exercises. 4 | 5 | ## Exercises 1, 2, 3, 4, and 8 6 | 7 | The code can be found [here](Hand.java). 8 | 9 | ## Exercise 5 10 | 11 | **Hints:** Your comparator class should have a field of an enumerated type, a way to set the value of the field, and use this value in the `compare` method. 12 | 13 | ## Exercise 6 14 | 15 | You can also clone the [Solitaire GitHub repo](https://github.com/prmr/Solitaire) and delete the Java files to give yourself a fresh start. 16 | 17 | ## Exercise 7 18 | 19 | Sample answers can be found in the [Solitaire GitHub repo](https://github.com/prmr/Solitaire). Note that in my solution I bypassed the creation of individual `SuitStack` classes and managed everything through a single `SuitStackManager`. However, it's not a bad idea to try it as suggested first. 20 | 21 | ## Exercise 9 22 | 23 | Since this is a model, small variants are possible. For example, the model would also be correct and useful without the interfaces `Edge` and `Node`. I added them because I thought that was a neat way to answer the question "What is a `GraphElement`?". I also only included a subset of the methods in `SelectionList` which I thought gave the best idea of how the class "worked", and left out a bunch of state querying methods. 24 | 25 | ![Solution to Exercise M02-8](m02-8.png) -------------------------------------------------------------------------------- /modules/answers/Answers-03.md: -------------------------------------------------------------------------------- 1 | # Module 3 - Answers 2 | 3 | Answers and answer sketches to the Module 3 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | The key insight here is that the state of the door (open or closed) needs to be reflected in the modeled abstract states. Note that the state diagram captures information on how the "start" button works. Because every state has a transition labeled "start", it means it's always possible to press the "start" button, it simply happens to do nothing except in the case where there's a balance and the door is closed. Not including the "start" self-transitions would actually be a different specification for the machine. It would mean that it is physically impossible to press the button except when it can start the machine (in the sense that the button is mechanically blocked or something). Assuming whoever uses the machine actually wants to get their dry laundry back, it makes sense to include an end state. 8 | 9 | ![](m03-1a.png) 10 | 11 | ## Exercise 2 12 | 13 | ![](m03-2.png) 14 | 15 | ## Exercise 3 16 | 17 | See the implemented [Card](https://github.com/prmr/Solitaire/blob/v0.3/src/ca/mcgill/cs/stg/solitaire/cards/Card.java) class. Note that there are different ways to implement the Flyweight collection. An alternative would be using lazy initialization of a double hash table. 18 | 19 | ## Exercise 4 20 | 21 | **Sketch:** You can implement a class `WorkingStack` that is a wrapper for a `java.util.Stack`. To record which card is visible or not, many different strategies are possible. For example, you could have a separate field `aVisible` of type `Set` in which you add cards as they become visible. The sample [WorkingStackManager](https://github.com/prmr/Solitaire/blob/v0.4/src/ca/mcgill/cs/stg/solitaire/model/WorkingStackManager.java) in the completed Solitaire application does things a bit differently, by managing all working stacks through a single class, which is slightly different from what is required for this exercise. 22 | 23 | ## Exercise 5 24 | 25 | The minimum necessary to realize the Singleton pattern is as follows: 26 | 27 | ```java 28 | public final class GameModel implements GameModelView 29 | { 30 | private static final GameModel INSTANCE = new GameModel(); 31 | 32 | /** 33 | * @return The singleton instance for this class. 34 | */ 35 | public static GameModel instance() 36 | { 37 | return INSTANCE; 38 | } 39 | } 40 | ``` 41 | 42 | ## Exercise 6 43 | 44 | See the fully implemented [GameModel](https://github.com/prmr/Solitaire/blob/v0.4/src/ca/mcgill/cs/stg/solitaire/model/GameModel.java) for a sample. 45 | -------------------------------------------------------------------------------- /modules/answers/Answers-04.md: -------------------------------------------------------------------------------- 1 | # Module 4 - Answers 2 | 3 | Answers and answer sketches to the Module 4 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | See [TestMath.java](../artifacts/module-04/module04/TestMath.java) -------------------------------------------------------------------------------- /modules/answers/Answers-05.md: -------------------------------------------------------------------------------- 1 | # Module 5 - Answers 2 | 3 | Answers and answer sketches to the Module 5 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | ![](m05-1.png) 8 | 9 | Note that the diagram skips over the initialization of the two `String` objects, which in any case is done under the covers by the compiler because of the use of string literals. It's also interesting to note that the object referenced by `string2` does *not* participate in the sequence at the level of abstraction modeled here. Finally, detailed calls to library methods 10 | are not normally part of sequence diagrams, but here it was interesting to show what the common `println` statement maps to. 11 | 12 | ## Exercise 2 13 | 14 | ![](m05-2.png) 15 | 16 | Things to note from this diagram: 17 | 18 | * The use of the somewhat "fake" `client` object provides a nice way to provide the context of the object sequence (that is, where the sequence occurs in the code); 19 | * The diagram does not include any detailed code logic, for example the fact that the bottom part of the sequence only occurs if `isEmpty()` returns `false`. That is not the point of a model. People who need that level of detail should look at the code directly. 20 | * The appropriate use of names really increases the clarity of the model. For example, here I annotated the return edge with the label `topMove`, then gave this name to the object of type `Move`. From this it should be fairly clear that it's the top move that's being undone, without having to rely of diagram notation. 21 | 22 | ## Exercise 3 23 | 24 | ![](m05-3.png) 25 | 26 | With this question we finally reached a level of complexity where the value of UML models starts to become more apparent. Here with a minimum of experience a developer would be able to see from the left of the diagram that the `EditorFrame` takes care for a bunch of object navigation and access, and from the right of the diagram that that actual `cut` functionality is realized through a close collaboration between an instance of `GraphPanel` and an instance of `Clipboard`. 27 | 28 | ## Exercise 4 29 | 30 | The class must be declared to implement `Cloneable`, and supply a `clone` method. Here we assume that 31 | it's acceptable to make a shallow copy of the card objects, since they are immutable and either unique or systematically tested 32 | for equality using `equals`. It would also have been a correct answer to deep-copy the card objects, since the design 33 | does not require them to be unique. 34 | 35 | ```java 36 | public class Hand implements Iterable, Cloneable 37 | { 38 | ... 39 | @Override 40 | public Hand clone() 41 | { 42 | try 43 | { 44 | Hand clone = (Hand) super.clone(); 45 | clone.aCards = new ArrayList<>(); 46 | for( Card card : aCards ) 47 | { 48 | clone.add(card); 49 | } 50 | return clone; 51 | } 52 | catch(CloneNotSupportedException e) 53 | { 54 | return null; 55 | } 56 | } 57 | ``` 58 | 59 | ![](m05-4.png) 60 | 61 | ## Exercise 5 62 | 63 | Here a deep copy of the decorated object is required, otherwise there would be two distinct decorators 64 | decorating the same object. To achieve multi-decoration, the proper way is to nest decorators. 65 | 66 | ```java 67 | @Override 68 | public ConferenceShow clone() 69 | { 70 | try 71 | { 72 | ConferenceShow clone = (ConferenceShow) super.clone(); 73 | clone.aDecorated = aDecorated.clone(); 74 | return clone; 75 | } 76 | catch (CloneNotSupportedException e) 77 | { 78 | return null; 79 | } 80 | } 81 | ``` 82 | 83 | ## Exercise 6 84 | 85 | Here I chose to create a separate class for the `CompositeCommand`, since there is no issue with leaking references if they 86 | are already well-encapsulated into the component commands. 87 | 88 | ```java 89 | public class CompositeCommand implements Command 90 | { 91 | private List aCommands = new ArrayList<>(); 92 | 93 | public CompositeCommand(Command... pCommands ) 94 | { 95 | for( Command command : pCommands ) 96 | { 97 | aCommands.add(command); 98 | } 99 | } 100 | 101 | @Override 102 | public void execute() 103 | { 104 | for( Command command : aCommands ) 105 | { 106 | command.execute(); 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | The final piece is to implement a factory for different types of composite commands, for example a `resetAndAdd` command: 113 | 114 | ```java 115 | public class Program 116 | { 117 | // Some code 118 | 119 | public void resetAndAdd(Show pShow, Day pDay) 120 | { 121 | assert pShow != null && pDay != null; 122 | Command command = new CompositeCommand( 123 | () -> Arrays.fill(aShows, NullShow.DEFAULT), 124 | () -> aShows[pDay.ordinal()] = pShow.clone()); 125 | aCommands.add(command); 126 | command.execute(); 127 | } 128 | ``` 129 | 130 | ## Exercise 7 131 | 132 | We define a decorator class as follows: 133 | 134 | ```java 135 | public class LoggedCommand implements Command 136 | { 137 | private final Command aCommand; 138 | private final String aMessage; 139 | 140 | public LoggedCommand(Command pCommand, String pMessage) 141 | { 142 | aCommand = pCommand; 143 | aMessage = pMessage; 144 | } 145 | 146 | @Override 147 | public void execute() 148 | { 149 | aCommand.execute(); 150 | // Decoration 151 | System.out.println(String.format("Executed command %s", aMessage)); 152 | } 153 | } 154 | ``` 155 | 156 | and to log a certain command, we wrap it as follows: 157 | 158 | ```java 159 | public class Program 160 | { 161 | // Some code 162 | 163 | public void add(Show pShow, Day pDay) 164 | { 165 | assert pShow != null && pDay != null; 166 | Command command = new LoggedCommand(() -> aShows[pDay.ordinal()] = pShow.clone(), "ADD"); 167 | aCommands.add(command); 168 | command.execute(); 169 | } 170 | ``` 171 | 172 | Here, overriding `toString()` in the various classes in the sample code would open up a whole range of possibilities 173 | for logging commands with more useful information. 174 | 175 | ## Exercise 8 176 | 177 | ![](m05-8.png) 178 | 179 | ## Exercise 9 180 | 181 | ![](m05-9.png) 182 | 183 | ## Exercise 10 184 | 185 | You will need two new classes, a `CompositeIcon` and a `ShiftedIcon`. 186 | 187 | ```java 188 | public class CompositeIcon implements Icon 189 | { 190 | private final List aIcons = new ArrayList<>(); 191 | 192 | public void addIcon(Icon pIcon) 193 | { 194 | aIcons.add(pIcon); 195 | } 196 | 197 | @Override 198 | public int getIconHeight() 199 | { 200 | int max = 0; 201 | for( Icon icon : aIcons ) 202 | { 203 | max = Math.max(max, icon.getIconHeight()); 204 | } 205 | return max; 206 | } 207 | 208 | @Override 209 | public int getIconWidth() 210 | { 211 | int max = 0; 212 | for( Icon icon : aIcons ) 213 | { 214 | max = Math.max(max, icon.getIconWidth()); 215 | } 216 | return max; 217 | } 218 | 219 | @Override 220 | public void paintIcon(Component pComponent, Graphics pGraphics, int pX, int pY) 221 | { 222 | for( Icon icon : aIcons ) 223 | { 224 | icon.paintIcon(pComponent, pGraphics, pX, pY); 225 | } 226 | } 227 | } 228 | ``` 229 | 230 | ```java 231 | public class ShiftedIcon implements Icon 232 | { 233 | private final Icon aShiftedIcon; 234 | private final int aX; 235 | private final int aY; 236 | 237 | public ShiftedIcon(Icon pIcon, int pX, int pY) 238 | { 239 | aShiftedIcon = pIcon; 240 | aX = pX; 241 | aY = pY; 242 | } 243 | 244 | 245 | @Override 246 | public int getIconHeight() 247 | { 248 | return aShiftedIcon.getIconHeight() + aY; 249 | } 250 | 251 | @Override 252 | public int getIconWidth() 253 | { 254 | return aShiftedIcon.getIconWidth() + aX; 255 | } 256 | 257 | @Override 258 | public void paintIcon(Component pComponent, Graphics pGraphics, int pX, int pY) 259 | { 260 | aShiftedIcon.paintIcon(pComponent, pGraphics, pX + aX, pY + aY); 261 | } 262 | } 263 | ``` 264 | 265 | With these two working it's just a matter of creating the desired icon within the constructor and callback in `HandPanel`: 266 | 267 | ```java 268 | HandPanel() 269 | { 270 | setBackground(CASINO_GREEN); 271 | add(aLabel); 272 | CompositeIcon icon = new CompositeIcon(); 273 | for( int i = 0; i < 13; i++ ) 274 | { 275 | icon.addIcon(new ShiftedIcon(CardImages.getBack(), 20*i, 5 * i)); 276 | } 277 | aLabel.setIcon(icon); 278 | } 279 | 280 | public void showHand(Card[] pHand) 281 | { 282 | Deck deck = new Deck(); 283 | deck.shuffle(); 284 | CompositeIcon icon = new CompositeIcon(); 285 | for( int i = 0; i < 13; i++ ) 286 | { 287 | icon.addIcon(new ShiftedIcon(CardImages.getCard(deck.draw()), 20*i, 5 * i)); 288 | } 289 | aLabel.setIcon(icon); 290 | } 291 | ``` 292 | 293 | -------------------------------------------------------------------------------- /modules/answers/Answers-06.md: -------------------------------------------------------------------------------- 1 | # Module 6 - Answers 2 | 3 | Answers and answer sketches to the Module 6 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | The easiest solution is to copy class `TextPanel` to something like `RomanPanel` and to update the string literals from English to Roman numerals (e.g., "Two" becomes "II"). You 8 | can leave zero as is, since the Romans were unaware of this concept (and the string for zero is not displayed anyways). To add the panel to the application, simply add this statement at the appropriate place 9 | in method `start`: 10 | 11 | ```java 12 | root.add(new RomanPanel(model), 0, 3, 1, 1); 13 | ``` 14 | 15 | The point of this exercise was to demonstrate how the Observer pattern makes it very easy to add/remove views without affecting the rest of the code. 16 | 17 | ## Exercise 2 18 | 19 | The main change is that the callback no longer has a parameter. This requires documenting how the concrete observers can access the model data. In the diagram this is represented by the aggregation to the `Model` and the parameter in the constructor, which clarifies how this aggregation is acquired. The other concrete observers are left out without loss of generality. 20 | 21 | ![](m06-2.png) 22 | 23 | In the code, besides updating the signature of the callback method, the number in the model needs to be accessed from the reference to the model, for example in `IntegerPanel`: 24 | 25 | ```java 26 | @Override 27 | public void newNumber() 28 | { 29 | aText.setText(new Integer(aModel.getNumber()).toString()); 30 | } 31 | ``` 32 | 33 | The sequence required to update the observers becomes a bit more complex: 34 | 35 | ![](m06-2b.png) 36 | 37 | ## Exercise 3 38 | 39 | The minimal code to exercise all parts of the pattern is shown below. The skeleton implements the last version of the design discussed in the case study. 40 | 41 | ```java 42 | public class Inventory 43 | { 44 | private List aItems = new ArrayList<>(); 45 | private List aAdditionObservers = new ArrayList<>(); 46 | private List aRemovalObservers = new ArrayList<>(); 47 | 48 | public static void main(String[] args) 49 | { 50 | Inventory inventory = new Inventory(); 51 | PieChart pieChart = new PieChart(); 52 | ListView listView = new ListView(); 53 | TransactionLogger logger = new TransactionLogger(); 54 | inventory.addAdditionObserver(pieChart); 55 | inventory.addAdditionObserver(listView); 56 | inventory.addAdditionObserver(logger); 57 | inventory.addRemovalObserver(pieChart); 58 | inventory.addRemovalObserver(listView); 59 | 60 | Item item1 = new Item("Stapler"); 61 | Item item2 = new Item("Toaster"); 62 | inventory.addItem(item1); 63 | inventory.addItem(item2); 64 | inventory.removeItem(item1); 65 | } 66 | 67 | public void addAdditionObserver( AdditionObserver pObserver ) 68 | { 69 | aAdditionObservers.add(pObserver); 70 | } 71 | 72 | public void addRemovalObserver( RemovalObserver pObserver ) 73 | { 74 | aRemovalObservers.add(pObserver); 75 | } 76 | 77 | public void addItem(Item pItem) 78 | { 79 | aItems.add(pItem); 80 | notifyAdditionObservers(pItem); 81 | } 82 | 83 | public void removeItem(Item pItem) 84 | { 85 | aItems.remove(pItem); 86 | notifyRemovalObservers(pItem); 87 | } 88 | 89 | private void notifyAdditionObservers(Item pItem) 90 | { 91 | for( AdditionObserver observer : aAdditionObservers ) 92 | { 93 | observer.itemAdded(pItem); 94 | } 95 | } 96 | 97 | private void notifyRemovalObservers(Item pItem) 98 | { 99 | for( RemovalObserver observer : aRemovalObservers ) 100 | { 101 | observer.itemRemoved(pItem); 102 | } 103 | } 104 | } 105 | 106 | interface AdditionObserver 107 | { 108 | void itemAdded(Item pItem); 109 | } 110 | 111 | interface RemovalObserver 112 | { 113 | void itemRemoved(Item pItem); 114 | } 115 | 116 | class Item 117 | { 118 | private String aName; 119 | 120 | public Item(String pName) 121 | { 122 | aName = pName; 123 | } 124 | 125 | @Override 126 | public String toString() 127 | { 128 | return aName; 129 | } 130 | } 131 | 132 | class PieChart implements AdditionObserver, RemovalObserver 133 | { 134 | @Override 135 | public void itemRemoved(Item pItem) 136 | { 137 | System.out.println("Removed " + pItem + " from PieChart"); 138 | } 139 | 140 | @Override 141 | public void itemAdded(Item pItem) 142 | { 143 | System.out.println("Added " + pItem + " to PieChart"); 144 | } 145 | } 146 | 147 | class ListView implements AdditionObserver, RemovalObserver 148 | { 149 | @Override 150 | public void itemRemoved(Item pItem) 151 | { 152 | System.out.println("Removed " + pItem + " from ListView"); 153 | } 154 | 155 | @Override 156 | public void itemAdded(Item pItem) 157 | { 158 | System.out.println("Added " + pItem + " to ListView"); 159 | } 160 | } 161 | 162 | class TransactionLogger implements AdditionObserver 163 | { 164 | @Override 165 | public void itemAdded(Item pItem) 166 | { 167 | System.out.println("Logged addition of " + pItem) ; 168 | } 169 | } 170 | ``` 171 | 172 | ## Exercise 4 173 | 174 | The class diagram. A useful variant would be to add dependencies between concrete observers (e.g., `DeckView`) and the `GameModel`, since the callbacks mostly access the game model. 175 | 176 | ![](m06-4a.png) 177 | 178 | The sequence diagram starts the sequence with a "client" calling `perform` on a `CardMove` target. This happens in the GUI code. Note that the model leaves out numerous details, among others the non-trivial logic in `GameModel.move(...)`, so as to focus on illustrating the behavior relevant to the observer pattern. Many instances of `GameModelListener` are left 179 | out to avoid cluttering the diagram. 180 | 181 | ![](m06-4b.png) 182 | 183 | ### Exercise 5 184 | 185 | The no-frills code below gets the job done. A variant using a lambda expression 186 | as handler is shown in comments. 187 | 188 | ```java 189 | public class DateButton extends Application 190 | { 191 | public static void main(String[] pArgs) 192 | { 193 | launch(pArgs); 194 | } 195 | 196 | @Override 197 | public void start(Stage pPrimaryStage) 198 | { 199 | Button button = new Button(LocalDateTime.now().toString()); 200 | button.setOnAction(new EventHandler() 201 | { 202 | @Override 203 | public void handle(ActionEvent arg0) 204 | { 205 | button.setText(LocalDateTime.now().toString()); 206 | } 207 | }); 208 | 209 | // Does exactly the same as the above statement, but using a lambda expression. 210 | // button.setOnAction((e) -> button.setText(LocalDateTime.now().toString())); 211 | 212 | pPrimaryStage.setScene(new Scene(button)); 213 | pPrimaryStage.show(); 214 | } 215 | } 216 | ``` -------------------------------------------------------------------------------- /modules/answers/Answers-08.md: -------------------------------------------------------------------------------- 1 | # Module 6 - Answers 2 | 3 | Answers and answer sketches to the Module 8 practice exercises. 4 | 5 | ## Exercise 1 and 2 6 | 7 | Other variants are possible. 8 | 9 | ![](m08-1.png) 10 | 11 | ![](m08-2.png) -------------------------------------------------------------------------------- /modules/answers/Answers-09.md: -------------------------------------------------------------------------------- 1 | # Module 9 - Answers 2 | 3 | Answers and answer sketches to the Module 8 practice exercises. 4 | 5 | ## Exercise 1 6 | 7 | You need to synchronize class `Box` to avoid data races and to ensure the visibility of new values across threads. 8 | 9 | ```java 10 | public class Box 11 | { 12 | private final Lock lock = new ReentrantLock(); 13 | 14 | private int aNumber = 0; 15 | 16 | public void increment() 17 | { 18 | lock.lock(); 19 | try { aNumber ++; } 20 | finally { lock.unlock(); } 21 | } 22 | 23 | public int get() 24 | { 25 | lock.lock(); 26 | try{ return aNumber; } 27 | finally { lock.unlock(); } 28 | } 29 | 30 | public static void main(String[] args) 31 | { 32 | final Box box = new Box(); 33 | new Thread(new Runnable() 34 | { 35 | public void run() 36 | { 37 | while(true) 38 | { 39 | box.increment(); 40 | } 41 | } 42 | }).start(); 43 | new Thread(new Runnable() 44 | { 45 | public void run() 46 | { 47 | try 48 | { 49 | while(true) 50 | { 51 | System.out.println(box.get()); 52 | Thread.sleep(1000); 53 | } 54 | } 55 | catch(InterruptedException e ) 56 | { 57 | return; 58 | } 59 | } 60 | }).start(); 61 | } 62 | } 63 | ``` 64 | 65 | ## Exercise 2 66 | 67 | This version should print the sequence of positive integers, one per second. 68 | 69 | ```java 70 | public class Box 71 | { 72 | private final Lock lock = new ReentrantLock(); 73 | private final Condition valueAvailable = lock.newCondition(); 74 | 75 | private int aNumber = 0; 76 | 77 | public void increment() 78 | { 79 | lock.lock(); 80 | try 81 | { 82 | aNumber ++; 83 | valueAvailable.signalAll(); 84 | } 85 | finally { lock.unlock(); } 86 | } 87 | 88 | public int get() 89 | { 90 | lock.lock(); 91 | try 92 | { 93 | valueAvailable.await(); 94 | return aNumber; 95 | } 96 | catch( InterruptedException e ) 97 | { 98 | return aNumber; 99 | } 100 | finally { lock.unlock(); } 101 | } 102 | 103 | public static void main(String[] args) 104 | { 105 | final Box box = new Box(); 106 | new Thread(new Runnable() 107 | { 108 | public void run() 109 | { 110 | try 111 | { 112 | while(true) 113 | { 114 | Thread.sleep(1000); 115 | box.increment(); 116 | 117 | } 118 | } 119 | catch( InterruptedException e) 120 | { 121 | return; 122 | } 123 | } 124 | }).start(); 125 | new Thread(new Runnable() 126 | { 127 | public void run() 128 | { 129 | while(true) 130 | { 131 | System.out.println(box.get()); 132 | } 133 | } 134 | }).start(); 135 | } 136 | } 137 | ``` 138 | 139 | ## Exercise 3 140 | 141 | While using a timer API would be superior, for this small exercise the following revised version of the `main` method will do the job: 142 | 143 | ```java 144 | public static void main(String[] args) 145 | { 146 | final Box box = new Box(); 147 | final Thread counter = new Thread(new Runnable() 148 | { 149 | public void run() 150 | { 151 | try 152 | { 153 | while(!Thread.interrupted()) 154 | { 155 | Thread.sleep(1000); 156 | box.increment(); 157 | 158 | } 159 | } 160 | catch( InterruptedException e) 161 | { 162 | return; 163 | } 164 | } 165 | }); 166 | final Thread printer = new Thread(new Runnable() 167 | { 168 | public void run() 169 | { 170 | while(!Thread.interrupted()) 171 | { 172 | System.out.println(box.get()); 173 | } 174 | } 175 | }); 176 | final Thread timer = new Thread(new Runnable() 177 | { 178 | public void run() 179 | { 180 | while( box.get() < 10 ) 181 | {} 182 | counter.interrupt(); 183 | printer.interrupt(); 184 | } 185 | }); 186 | timer.start(); 187 | counter.start(); 188 | printer.start(); 189 | } 190 | ``` -------------------------------------------------------------------------------- /modules/answers/EnumeratedCard.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | public enum EnumeratedCard 4 | { 5 | ACE_CLUBS, TWO_CLUBS, THREE_CLUBS, FOUR_CLUBS, FIVE_CLUBS, SIX_CLUBS, SEVEN_CLUBS, 6 | EIGHT_CLUBS, NINE_CLUBS, TEN_CLUBS, JACK_CLUBS, QUEEN_CLUBS, KING_CLUBS, 7 | ACE_DIAMONDS, TWO_DIAMONDS, THREE_DIAMONDS; 8 | 9 | /** 10 | * A card's suit. 11 | */ 12 | public enum Suit 13 | { 14 | CLUBS, DIAMONDS, SPADES, HEARTS; 15 | } 16 | 17 | /** 18 | * A card's rank. 19 | */ 20 | public enum Rank 21 | { ACE, TWO, THREE, FOUR, FIVE, SIX, 22 | SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; 23 | } 24 | 25 | public Suit getSuit() 26 | { 27 | return Suit.values()[ordinal() / Rank.values().length]; 28 | } 29 | 30 | public Rank getRank() 31 | { 32 | return Rank.values()[ordinal() % Rank.values().length]; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /modules/answers/Hand.java: -------------------------------------------------------------------------------- 1 | package comp303m02; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | import comp303m02.Card.Rank; 9 | 10 | /** 11 | * A collection of cards in a player's hand. 12 | */ 13 | public class Hand implements Iterable, Comparable 14 | { 15 | private final List aCards = new ArrayList<>(); 16 | private final int aMaxCards; 17 | 18 | /** 19 | * Creates a new, empty hand, which can hold 20 | * a maximum of pMaxCards. 21 | * 22 | * @param pMaxCards The maximum number of cards allowed in this hand. 23 | * @pre pMaxCards > 0; 24 | */ 25 | public Hand(int pMaxCards) 26 | { 27 | assert pMaxCards > 0; 28 | aMaxCards = pMaxCards; 29 | } 30 | 31 | /** 32 | * Add pCards to the hand. 33 | * @param pCard The card to add. 34 | * @pre !isFull() 35 | * @pre pCard != null; 36 | */ 37 | public void add(Card pCard) 38 | { 39 | assert pCard != null; 40 | assert !isFull(); 41 | aCards.add(pCard); 42 | } 43 | 44 | /** 45 | * @return True if the number of cards in the hand 46 | * is the maximum number of cards allowable, as specified 47 | * in the constructor. 48 | */ 49 | public boolean isFull() 50 | { 51 | return aCards.size() == aMaxCards; 52 | } 53 | 54 | /** 55 | * @return True if there are no cards in this hand. 56 | */ 57 | public boolean isEmpty() 58 | { 59 | return aCards.size() == 0; 60 | } 61 | 62 | /** 63 | * Removes pCards if it is in the hand. If it is not in the 64 | * hand, does nothing. 65 | * 66 | * @param pCard The card to remove. 67 | * @pre pCards != null; 68 | */ 69 | public void remove(Card pCard) 70 | { 71 | assert pCard != null; 72 | aCards.remove(pCard); 73 | } 74 | 75 | /** 76 | * @param pCard A card to check for containment. 77 | * @return True if pCard is a card in this hand. 78 | * @pre pCard != null 79 | */ 80 | public boolean contains(Card pCard) 81 | { 82 | assert pCard != null; 83 | return aCards.contains(pCard); 84 | } 85 | 86 | @Override 87 | public Iterator iterator() 88 | { 89 | return aCards.iterator(); 90 | } 91 | 92 | @Override 93 | public int compareTo(Hand pHand) 94 | { 95 | return aCards.size() - pHand.aCards.size(); 96 | } 97 | 98 | public static Comparator createAscendingComparator() 99 | { 100 | return new Comparator() { 101 | 102 | @Override 103 | public int compare(Hand pHand1, Hand pHand2) 104 | { 105 | return pHand1.aCards.size() - pHand2.aCards.size(); 106 | }}; 107 | } 108 | 109 | public static Comparator createDescendingComparator() 110 | { 111 | return new Comparator() { 112 | 113 | @Override 114 | public int compare(Hand pHand1, Hand pHand2) 115 | { 116 | return pHand2.aCards.size() - pHand1.aCards.size(); 117 | }}; 118 | } 119 | 120 | /** 121 | * Creates a comparator that compares hands in terms of ascending number 122 | * of cards of rank pRank in the hand. 123 | * 124 | * @param pRank The rank to test against. 125 | * @return A new Comparator instance that can compare by number 126 | * of cards of the specified rank. 127 | */ 128 | public static Comparator createByRankComparator(Rank pRank) 129 | { 130 | return new Comparator() 131 | { 132 | @Override 133 | public int compare(Hand pHand1, Hand pHand2) 134 | { 135 | return countCards(pHand1, pRank) - countCards(pHand2, pRank); 136 | } 137 | 138 | private int countCards(Hand pHand, Rank pRank) 139 | { 140 | int total = 0; 141 | for( Card card : pHand) 142 | { 143 | if( card.getRank() == pRank ) 144 | { 145 | total++; 146 | } 147 | } 148 | return total; 149 | } 150 | }; 151 | 152 | 153 | } 154 | 155 | /** 156 | * This is the driver program. 157 | * @param args 158 | */ 159 | public static void main(String[] args) 160 | { 161 | Hand hand1 = new Hand(5); 162 | Hand hand2 = new Hand(5); 163 | Deck deck = new Deck(); 164 | deck.shuffle(); 165 | hand1.add(deck.draw()); 166 | hand2.add(deck.draw()); 167 | hand2.add(deck.draw()); 168 | System.out.println(hand1.compareTo(hand2)); 169 | System.out.println(hand2.compareTo(hand1)); 170 | System.out.println(Hand.createAscendingComparator().compare(hand1, hand2)); 171 | System.out.println(Hand.createDescendingComparator().compare(hand1, hand2)); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /modules/answers/MultiDeck.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * A class that represents a deck of cards composed of multiple decks 5 | * of cards. This version does not implement the copy-constructor 6 | * part the exercise. 7 | * 8 | * @author Martin P. Robillard 9 | */ 10 | public class MultiDeck 11 | { 12 | private Deck[] aDecks; 13 | 14 | /** 15 | * Creates an initialized, shuffled multi-deck from pNumberOfDecks 16 | * decks. 17 | * @param pNumberOfDecks The number of decks in the multi-deck 18 | * @pre pNumberOfDecks > 0 19 | */ 20 | public MultiDeck(int pNumberOfDecks) 21 | { 22 | assert pNumberOfDecks > 0; 23 | aDecks = new Deck[pNumberOfDecks]; 24 | for( int i =0; i < aDecks.length; i++ ) 25 | { 26 | aDecks[i] = new Deck(); 27 | aDecks[i].shuffle(); 28 | } 29 | } 30 | 31 | /** 32 | * @return The next card in the multi-deck 33 | * @pre !isEmpty() 34 | */ 35 | public Card draw() 36 | { 37 | assert !isEmpty(); 38 | for( Deck deck : aDecks ) 39 | { 40 | if( deck.isEmpty() ) 41 | { 42 | continue; 43 | } 44 | return deck.draw(); 45 | } 46 | assert false; 47 | return null; 48 | } 49 | 50 | /** 51 | * @return True if all decks in this multi-deck are empty 52 | */ 53 | public boolean isEmpty() 54 | { 55 | for( Deck deck : aDecks ) 56 | { 57 | if( !deck.isEmpty() ) 58 | { 59 | return false; 60 | } 61 | } 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /modules/answers/m01-a-multideck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m01-a-multideck.png -------------------------------------------------------------------------------- /modules/answers/m02-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m02-8.png -------------------------------------------------------------------------------- /modules/answers/m03-1a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m03-1a.png -------------------------------------------------------------------------------- /modules/answers/m03-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m03-2.png -------------------------------------------------------------------------------- /modules/answers/m05-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-1.png -------------------------------------------------------------------------------- /modules/answers/m05-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-2.png -------------------------------------------------------------------------------- /modules/answers/m05-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-3.png -------------------------------------------------------------------------------- /modules/answers/m05-3.sequence.jet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | x 9 | 10 | 11 | 50.0 12 | 13 | 14 | 15 | height 16 | 17 | 18 | 630.0 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | client: 28 | 29 | 30 | 31 | 32 | 33 | 34 | x 35 | 36 | 37 | 82.0 38 | 39 | 40 | 41 | y 42 | 43 | 44 | 70.0 45 | 46 | 47 | 48 | height 49 | 50 | 51 | 540.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | x 70 | 71 | 72 | 170.0 73 | 74 | 75 | 76 | height 77 | 78 | 79 | 630.0 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | :EditorFrame 89 | 90 | 91 | 92 | 93 | 94 | 95 | x 96 | 97 | 98 | 202.0 99 | 100 | 101 | 102 | y 103 | 104 | 105 | 94.0 106 | 107 | 108 | 109 | height 110 | 111 | 112 | 496.0 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | x 129 | 130 | 131 | 210.0 132 | 133 | 134 | 135 | y 136 | 137 | 138 | 123.0 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | x 157 | 158 | 159 | 310.0 160 | 161 | 162 | 163 | width 164 | 165 | 166 | 180.0 167 | 168 | 169 | 170 | height 171 | 172 | 173 | 630.0 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | aTabbedPane:JTabbedPane 183 | 184 | 185 | 186 | 187 | 188 | 189 | x 190 | 191 | 192 | 392.0 193 | 194 | 195 | 196 | y 197 | 198 | 199 | 177.0 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | x 218 | 219 | 220 | 510.0 221 | 222 | 223 | 224 | width 225 | 226 | 227 | 160.0 228 | 229 | 230 | 231 | height 232 | 233 | 234 | 630.0 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | graphFrame:GraphFrame 244 | 245 | 246 | 247 | 248 | 249 | 250 | x 251 | 252 | 253 | 582.0 254 | 255 | 256 | 257 | y 258 | 259 | 260 | 231.0 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | x 279 | 280 | 281 | 690.0 282 | 283 | 284 | 285 | width 286 | 287 | 288 | 120.0 289 | 290 | 291 | 292 | height 293 | 294 | 295 | 630.0 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | panel:GraphPanel 305 | 306 | 307 | 308 | 309 | 310 | 311 | x 312 | 313 | 314 | 742.0 315 | 316 | 317 | 318 | y 319 | 320 | 321 | 285.0 322 | 323 | 324 | 325 | height 326 | 327 | 328 | 231.0 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | x 345 | 346 | 347 | 750.0 348 | 349 | 350 | 351 | y 352 | 353 | 354 | 333.0 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | x 371 | 372 | 373 | 750.0 374 | 375 | 376 | 377 | y 378 | 379 | 380 | 446.0 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | x 397 | 398 | 399 | 742.0 400 | 401 | 402 | 403 | y 404 | 405 | 406 | 540.0 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | x 425 | 426 | 427 | 850.0 428 | 429 | 430 | 431 | height 432 | 433 | 434 | 630.0 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | :Clipboard 444 | 445 | 446 | 447 | 448 | 449 | 450 | x 451 | 452 | 453 | 882.0 454 | 455 | 456 | 457 | y 458 | 459 | 460 | 309.0 461 | 462 | 463 | 464 | height 465 | 466 | 467 | 187.0 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | x 484 | 485 | 486 | 890.0 487 | 488 | 489 | 490 | y 491 | 492 | 493 | 392.0 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | cut() 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | noCurrentGraphFrame() 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | getSelectedComponent() 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | graphFrame 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | getGraphPanel() 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | panel 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | cut() 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | cut(panel) 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | getSelectionList() 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | selectionList 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | copy(selectionList) 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | removeSelected() 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | repaint() 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | -------------------------------------------------------------------------------- /modules/answers/m05-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-4.png -------------------------------------------------------------------------------- /modules/answers/m05-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-8.png -------------------------------------------------------------------------------- /modules/answers/m05-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m05-9.png -------------------------------------------------------------------------------- /modules/answers/m06-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m06-2.png -------------------------------------------------------------------------------- /modules/answers/m06-2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m06-2b.png -------------------------------------------------------------------------------- /modules/answers/m06-4a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m06-4a.png -------------------------------------------------------------------------------- /modules/answers/m06-4b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m06-4b.png -------------------------------------------------------------------------------- /modules/answers/m08-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m08-1.png -------------------------------------------------------------------------------- /modules/answers/m08-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/answers/m08-2.png -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card1.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * Allows us to manipulate values/objects 5 | * that map to the real-world concept of 6 | * of a "card" through a type that directly 7 | * represents that concept. Makes for easier- 8 | * to-understand programs than just using an 9 | * int. 10 | */ 11 | public class Card1 12 | { 13 | public int aId = 0; 14 | } 15 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card2.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * Improves (?) on Card1 by trying to hide our decision 5 | * concerning the internal representation of a card. 6 | * A small example of information hiding. 7 | */ 8 | public class Card2 9 | { 10 | private int aId = 0; 11 | 12 | /** 13 | * @param pCardId [0,52] inclusive 14 | */ 15 | public Card2(int pCardId) 16 | { 17 | aId = pCardId; 18 | } 19 | 20 | /** 21 | * @return [0,52] the id of the card 22 | */ 23 | public int getId() 24 | { 25 | return aId; 26 | } 27 | 28 | /** 29 | * @param pId [0,52] the id of the card 30 | */ 31 | public void setId(int pId) 32 | { 33 | aId = pId; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card3.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * Improves on Card2 by really hiding our decision 5 | * concerning the internal representation of a card, 6 | * and allowing access to the card's representation in 7 | * terms of concepts in the problem space, as opposed 8 | * to the internal representation. 9 | * A better example of information hiding. 10 | */ 11 | public class Card3 12 | { 13 | public static final String[] RANKS = {"Ace", "Two", "Three", "Four", "Five", 14 | "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}; 15 | 16 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 17 | 18 | private int aId = 0; 19 | 20 | /** 21 | * @param pRank The index of the rank in RANKS 22 | * @param pSuit The index of the suit in SUITS 23 | */ 24 | public Card3(int pRank, int pSuit) 25 | { 26 | aId = pSuit * RANKS.length + pRank; 27 | } 28 | 29 | /** 30 | * @return The index in RANKS corresponding to the rank of the card. 31 | */ 32 | public int getRank() 33 | { 34 | return aId % RANKS.length; 35 | } 36 | 37 | /** 38 | * @return The index in SUITS corresponding to the suit of the card. 39 | */ 40 | public int getSuit() 41 | { 42 | return aId / RANKS.length; 43 | } 44 | 45 | /** 46 | * Assigns a new rank to the card. 47 | * @param pRank The new rank. 48 | */ 49 | public void setRank(int pRank) 50 | { 51 | aId = getSuit() * RANKS.length + pRank; 52 | } 53 | 54 | /** 55 | * Assigns a new suit to the card. 56 | * @param pSuit The new suit. 57 | */ 58 | public void setSuit(int pSuit) 59 | { 60 | aId = pSuit * RANKS.length + getRank(); 61 | } 62 | 63 | @Override 64 | public String toString() 65 | { 66 | return RANKS[getRank()] + " of " + SUITS[getSuit()]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card4.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * Shows the benefits of information hiding. We change the 5 | * internal representation of a card from Card3 but the class's 6 | * interface remains the same and Client does not have to change. 7 | */ 8 | public class Card4 9 | { 10 | public static final String[] RANKS = {"Ace", "Two", "Three", "Four", "Five", 11 | "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}; 12 | 13 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 14 | 15 | private int aRank = 0; 16 | private int aSuit = 0; 17 | 18 | /** 19 | * @param pRank The index of the rank in RANKS 20 | * @param pSuit The index of the suit in SUITS 21 | */ 22 | public Card4(int pRank, int pSuit) 23 | { 24 | aRank = pRank; 25 | aSuit = pSuit; 26 | } 27 | 28 | /** 29 | * @return The index in RANKS corresponding to the rank of the card. 30 | */ 31 | public int getRank() 32 | { 33 | return aRank; 34 | } 35 | 36 | /** 37 | * @return The index in SUITS corresponding to the suit of the card. 38 | */ 39 | public int getSuit() 40 | { 41 | return aSuit; 42 | } 43 | 44 | /** 45 | * Assigns a new rank to the card. 46 | * @param pRank The new rank. 47 | */ 48 | public void setRank(int pRank) 49 | { 50 | aRank = pRank; 51 | } 52 | 53 | /** 54 | * Assigns a new suit to the card. 55 | * @param pSuit The new suit. 56 | */ 57 | public void setSuit(int pSuit) 58 | { 59 | aSuit = pSuit; 60 | } 61 | 62 | @Override 63 | public String toString() 64 | { 65 | return RANKS[getRank()] + " of " + SUITS[getSuit()]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card5.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * With this version we further improve encapsulation by 5 | * constraining values for ranks and suits to instances 6 | * of an enumerated type in a way that is backward compatible 7 | * for the client. 8 | */ 9 | public class Card5 10 | { 11 | /** 12 | * A card's rank. 13 | */ 14 | public enum Rank 15 | { ACE, TWO, THREE, FOUR, FIVE, SIX, 16 | SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; 17 | } 18 | 19 | /** 20 | * A card's suit. 21 | */ 22 | public enum Suit 23 | { 24 | CLUBS, DIAMONDS, SPADES, HEARTS 25 | } 26 | 27 | public static final String[] RANKS = {"Ace", "Two", "Three", "Four", "Five", 28 | "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}; 29 | 30 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 31 | 32 | private Rank aRank; 33 | private Suit aSuit; 34 | 35 | /** 36 | * @param pRank The index of the rank in RANKS 37 | * @param pSuit The index of the suit in SUITS 38 | */ 39 | public Card5(int pRank, int pSuit) 40 | { 41 | aRank = Rank.values()[pRank]; 42 | aSuit = Suit.values()[pSuit]; 43 | } 44 | 45 | /** 46 | * @return The index in RANKS corresponding to the rank of the card. 47 | */ 48 | public int getRank() 49 | { 50 | return aRank.ordinal(); 51 | } 52 | 53 | /** 54 | * @return The index in SUITS corresponding to the suit of the card. 55 | */ 56 | public int getSuit() 57 | { 58 | return aSuit.ordinal(); 59 | } 60 | 61 | /** 62 | * Assigns a new rank to the card. 63 | * @param pRank The new rank. 64 | */ 65 | public void setRank(int pRank) 66 | { 67 | aRank = Rank.values()[pRank]; 68 | } 69 | 70 | /** 71 | * Assigns a new suit to the card. 72 | * @param pSuit The new suit. 73 | */ 74 | public void setSuit(int pSuit) 75 | { 76 | aSuit = Suit.values()[pSuit]; 77 | } 78 | 79 | @Override 80 | public String toString() 81 | { 82 | return aRank + " of " + aSuit; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card6.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * With this version we break the client (force it 5 | * to adapt), but reap the major benefit of strengthening 6 | * the interface to the class. This version implements the 7 | * "defensive programming" approach to defend against 8 | * null values. 9 | */ 10 | public class Card6 11 | { 12 | /** 13 | * A card's rank. 14 | */ 15 | public enum Rank 16 | { ACE, TWO, THREE, FOUR, FIVE, SIX, 17 | SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; 18 | } 19 | 20 | /** 21 | * A card's suit. 22 | */ 23 | public enum Suit 24 | { 25 | CLUBS, DIAMONDS, SPADES, HEARTS 26 | } 27 | 28 | public static final String[] RANKS = {"Ace", "Two", "Three", "Four", "Five", 29 | "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}; 30 | 31 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 32 | 33 | private Rank aRank; 34 | private Suit aSuit; 35 | 36 | /** 37 | * @param pRank The index of the rank in RANKS 38 | * @param pSuit The index of the suit in SUITS 39 | */ 40 | public Card6(Rank pRank, Suit pSuit) 41 | { 42 | if( pRank == null || pSuit == null ) 43 | { 44 | throw new IllegalArgumentException(); 45 | } 46 | aRank = pRank; 47 | aSuit = pSuit; 48 | } 49 | 50 | /** 51 | * @return The index in RANKS corresponding to the rank of the card. 52 | */ 53 | public Rank getRank() 54 | { 55 | return aRank; 56 | } 57 | 58 | /** 59 | * @return The index in SUITS corresponding to the suit of the card. 60 | */ 61 | public Suit getSuit() 62 | { 63 | return aSuit; 64 | } 65 | 66 | /** 67 | * Assigns a new rank to the card. 68 | * @param pRank The new rank. 69 | */ 70 | public void setRank(Rank pRank) 71 | { 72 | aRank = pRank; 73 | } 74 | 75 | /** 76 | * Assigns a new suit to the card. 77 | * @param pSuit The new suit. 78 | */ 79 | public void setSuit(Suit pSuit) 80 | { 81 | aSuit = pSuit; 82 | } 83 | 84 | @Override 85 | public String toString() 86 | { 87 | return aRank + " of " + aSuit; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Card7.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | /** 4 | * Same as version 6 but with design by contract 5 | * instead of defensive programming. 6 | */ 7 | public class Card7 8 | { 9 | /** 10 | * A card's rank. 11 | */ 12 | public enum Rank 13 | { ACE, TWO, THREE, FOUR, FIVE, SIX, 14 | SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; 15 | } 16 | 17 | /** 18 | * A card's suit. 19 | */ 20 | public enum Suit 21 | { 22 | CLUBS, DIAMONDS, SPADES, HEARTS 23 | } 24 | 25 | public static final String[] RANKS = {"Ace", "Two", "Three", "Four", "Five", 26 | "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}; 27 | 28 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 29 | 30 | private Rank aRank; // Invariant: != null 31 | private Suit aSuit; // Invariant: != null 32 | 33 | /** 34 | * @param pRank The index of the rank in RANKS 35 | * @param pSuit The index of the suit in SUITS 36 | * @pre pRank != null && pSuit != null 37 | */ 38 | public Card7(Rank pRank, Suit pSuit) 39 | { 40 | assert pRank != null && pSuit != null; 41 | aRank = pRank; 42 | aSuit = pSuit; 43 | } 44 | 45 | /** 46 | * @return The index in RANKS corresponding to the rank of the card. 47 | * @post return != null 48 | */ 49 | public Rank getRank() 50 | { 51 | return aRank; 52 | } 53 | 54 | /** 55 | * @return The index in SUITS corresponding to the suit of the card. 56 | * @post return != null 57 | */ 58 | public Suit getSuit() 59 | { 60 | return aSuit; 61 | } 62 | 63 | /** 64 | * Assigns a new rank to the card. 65 | * @param pRank The new rank. 66 | * @pre pRank != null 67 | */ 68 | public void setRank(Rank pRank) 69 | { 70 | aRank = pRank; 71 | } 72 | 73 | /** 74 | * Assigns a new suit to the card. 75 | * @param pSuit The new suit. 76 | * @pre pSuit != null 77 | */ 78 | public void setSuit(Suit pSuit) 79 | { 80 | aSuit = pSuit; 81 | } 82 | 83 | @Override 84 | public String toString() 85 | { 86 | return aRank + " of " + aSuit; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /modules/artifacts/module-01/comp303/Client.java: -------------------------------------------------------------------------------- 1 | package comp303; 2 | 3 | import comp303.Card.Rank; 4 | import comp303.Card.Suit; 5 | 6 | /** This client class is a stand in for any client code. 7 | * This version is adapted to work with Card7 8 | */ 9 | public final class Client 10 | { 11 | private Client() 12 | {} 13 | 14 | /** 15 | * @param pArgs Not used here 16 | */ 17 | public static void main(String[] pArgs) 18 | { 19 | for( Rank rank : Rank.values() ) 20 | { 21 | for( Suit suit : Suit.values() ) 22 | { 23 | System.out.println(new Card(rank,suit)); 24 | } 25 | } 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /modules/artifacts/module-02/comp303m02/Card.java: -------------------------------------------------------------------------------- 1 | package comp303m02; 2 | 3 | import java.util.Comparator; 4 | 5 | public class Card implements Comparable 6 | { 7 | static class CompareBySuitFirst implements Comparator 8 | { 9 | @Override 10 | public int compare(Card pCard1, Card pCard2) 11 | { 12 | if( pCard1.getSuit() == pCard2.getSuit() ) 13 | { 14 | return pCard1.getRank().compareTo(pCard2.getRank()); 15 | } 16 | else 17 | { 18 | return pCard1.getSuit().ordinal() - pCard2.getSuit().ordinal(); 19 | } 20 | } 21 | } 22 | 23 | public static Comparator createByRankComparator() 24 | { 25 | return new Comparator() { 26 | 27 | @Override 28 | public int compare(Card pCard1, Card pCard2) 29 | { 30 | if( pCard1.getRank() == pCard2.getRank() ) 31 | { 32 | return pCard1.getSuit().compareTo(pCard2.getSuit()); 33 | } 34 | else 35 | { 36 | return pCard1.getRank().ordinal() - pCard2.getRank().ordinal(); 37 | } 38 | }}; 39 | } 40 | 41 | /** 42 | * A card's rank. The Rank object is a part of a Card object. 43 | */ 44 | public enum Rank 45 | { ACE, TWO, THREE, FOUR, FIVE, SIX, 46 | SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING; 47 | } 48 | 49 | /** 50 | * A card's suit. 51 | */ 52 | public enum Suit 53 | { 54 | CLUBS, DIAMONDS, SPADES, HEARTS; 55 | 56 | enum Color 57 | { 58 | RED, BLACK; 59 | } 60 | 61 | public Color getColor() 62 | { 63 | if( this == CLUBS || this == SPADES ) 64 | { 65 | return Color.BLACK; 66 | } 67 | else 68 | { 69 | return Color.RED; 70 | } 71 | } 72 | 73 | public String toString() 74 | { 75 | return name().substring(0,1) + name().substring(1, name().length()).toLowerCase(); 76 | } 77 | } 78 | 79 | public static final String[] SUITS = {"Clubs", "Diamonds", "Spades", "Hearts"}; 80 | 81 | private final Rank aRank; 82 | private final Suit aSuit; 83 | 84 | /** 85 | * @param pRank The index of the rank in RANKS 86 | * @param pSuit The index of the suit in SUITS 87 | * @pre pRank != null && pSuit != null 88 | */ 89 | public Card(Rank pRank, Suit pSuit) 90 | { 91 | assert pRank != null && pSuit != null; 92 | aRank = pRank; 93 | aSuit = pSuit; 94 | } 95 | 96 | public Card( Card pCard ) 97 | { 98 | aRank = pCard.aRank; 99 | aSuit = pCard.aSuit; 100 | } 101 | 102 | /** 103 | * @return The index in RANKS corresponding to the rank of the card. 104 | */ 105 | public Rank getRank() 106 | { 107 | return aRank; 108 | } 109 | 110 | /** 111 | * @return The index in SUITS corresponding to the suit of the card. 112 | */ 113 | public Suit getSuit() 114 | { 115 | return aSuit; 116 | } 117 | 118 | @Override 119 | public String toString() 120 | { 121 | return getRank() + " of " + getSuit(); 122 | } 123 | 124 | public int compareTo(Card pCard) 125 | { 126 | if( getSuit() == pCard.getSuit() ) 127 | { 128 | return getRank().compareTo(pCard.getRank()); 129 | } 130 | else 131 | { 132 | return getSuit().ordinal() - pCard.getSuit().ordinal(); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /modules/artifacts/module-02/comp303m02/Client.java: -------------------------------------------------------------------------------- 1 | package comp303m02; 2 | 3 | /** 4 | * Stand-in for the rest of the program. 5 | * 6 | */ 7 | public class Client 8 | { 9 | public static void main(String[] args) 10 | { 11 | Deck deck = new Deck(); 12 | deck.shuffle(); 13 | 14 | for( Card card : deck) 15 | { 16 | System.out.println(card); 17 | } 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /modules/artifacts/module-02/comp303m02/Deck.java: -------------------------------------------------------------------------------- 1 | package comp303m02; 2 | 3 | import java.util.Collections; 4 | import java.util.Comparator; 5 | import java.util.Iterator; 6 | import java.util.Stack; 7 | 8 | import comp303m02.Card.Rank; 9 | import comp303m02.Card.Suit; 10 | 11 | /** 12 | * A Deck of playing cards. 13 | * 14 | */ 15 | public final class Deck implements Iterable 16 | { 17 | private Stack aCards = new Stack<>(); 18 | 19 | public static void main(String[] args) 20 | { 21 | Deck deck = new Deck(); 22 | deck.shuffle(); 23 | } 24 | 25 | @Override 26 | public Iterator iterator() 27 | { 28 | return aCards.iterator(); 29 | } 30 | 31 | public void shuffle() 32 | { 33 | aCards.clear(); 34 | for(Suit suit : Suit.values()) 35 | { 36 | for(Rank rank : Rank.values()) 37 | { 38 | aCards.push(new Card(rank,suit)); 39 | } 40 | } 41 | Collections.shuffle(aCards); 42 | 43 | } 44 | 45 | /* Three different options are shown in parallel here. Clearly in a real program 46 | * you would select only one. 47 | */ 48 | public void sort() 49 | { 50 | Collections.sort(aCards, new Comparator() { 51 | 52 | @Override 53 | public int compare(Card pCard1, Card pCard2) 54 | { 55 | if( pCard1.getRank() == pCard2.getRank() ) 56 | { 57 | return pCard1.getSuit().compareTo(pCard2.getSuit()); 58 | } 59 | else 60 | { 61 | return pCard1.getRank().ordinal() - pCard2.getRank().ordinal(); 62 | } 63 | }}); 64 | 65 | Collections.sort(aCards, (pCard1, pCard2) -> pCard1.getRank().compareTo(pCard2.getRank())); 66 | 67 | Collections.sort(aCards, new Card.CompareBySuitFirst()); 68 | } 69 | 70 | public Card draw() 71 | { 72 | return aCards.pop(); 73 | } 74 | 75 | public boolean isEmpty() 76 | { 77 | return aCards.isEmpty(); 78 | } 79 | 80 | public Card peek() 81 | { 82 | assert !isEmpty(); 83 | return aCards.peek(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /modules/artifacts/module-02/comp303m02/Hand.java: -------------------------------------------------------------------------------- 1 | package comp303m02; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | /** 8 | * A collection of cards in a player's hand. 9 | * Incomplete. 10 | */ 11 | public class Hand implements Iterable 12 | { 13 | private final List aCards = new ArrayList<>(); 14 | 15 | public void add(Card pCard) 16 | { 17 | assert pCard != null; 18 | aCards.add(pCard); 19 | } 20 | 21 | @Override 22 | public Iterator iterator() 23 | { 24 | return aCards.iterator(); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /modules/artifacts/module-04/module04/TestMath.java: -------------------------------------------------------------------------------- 1 | package module04; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Tests for Math.abs and Math.min. The methods 9 | * are overloaded to support different primitive types. 10 | * These tests are only for the integer version. 11 | * 12 | * Note how each different class of input is 13 | * tested in a different unit test. 14 | */ 15 | public class TestMath 16 | { 17 | @Test 18 | public void testAbsZero() 19 | { 20 | assertEquals(0, Math.abs(0)); 21 | } 22 | 23 | @Test 24 | public void testAbsPositive() 25 | { 26 | assertEquals(5, Math.abs(5)); 27 | } 28 | 29 | @Test 30 | public void testAbsNegative() 31 | { 32 | assertEquals(5, Math.abs(-5)); 33 | } 34 | 35 | @Test 36 | public void testAbsMaxInt() 37 | { 38 | assertEquals(Integer.MAX_VALUE, Math.abs(Integer.MAX_VALUE)); 39 | } 40 | 41 | /* 42 | * If the oracle seems wrong, see: 43 | * https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#abs-int- 44 | */ 45 | @Test 46 | public void testAbsMinInt() 47 | { 48 | assertEquals(Integer.MIN_VALUE, Math.abs(Integer.MIN_VALUE)); 49 | } 50 | 51 | @Test 52 | public void testMinFirst() 53 | { 54 | assertEquals(1, Math.min(1, 2)); 55 | assertEquals(-2, Math.min(-2, -1)); 56 | assertEquals(Integer.MIN_VALUE, Math.min(Integer.MIN_VALUE, 0)); 57 | } 58 | 59 | @Test 60 | public void testMinSecond() 61 | { 62 | assertEquals(1, Math.min(2, 1)); 63 | assertEquals(-2, Math.min(-1, -2)); 64 | assertEquals(Integer.MIN_VALUE, Math.min(0, Integer.MIN_VALUE)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /modules/artifacts/module-06/module6/LuckyNumber.java: -------------------------------------------------------------------------------- 1 | package module6; 2 | 3 | 4 | import java.awt.GridLayout; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.util.ArrayList; 8 | 9 | import javax.swing.JFrame; 10 | import javax.swing.JPanel; 11 | import javax.swing.JSlider; 12 | import javax.swing.JTextField; 13 | import javax.swing.event.ChangeEvent; 14 | import javax.swing.event.ChangeListener; 15 | 16 | /** 17 | * @author Martin Robillard Revised 9 October 2014 18 | * 19 | * Class to demonstrate the Observer design pattern 20 | * in Java. Note that there are many ways to implement an 21 | * Observer design pattern, and that this is just one of them. 22 | */ 23 | @SuppressWarnings("serial") 24 | public class LuckyNumber extends JFrame 25 | { 26 | /** 27 | * Builds and launches the GUI. 28 | */ 29 | public LuckyNumber() 30 | { 31 | setTitle("Lucky Number"); 32 | // The first parameter below is the number of 33 | // rows in the main window. To add one, change 3 to 4. 34 | setLayout(new GridLayout(3, 1)); 35 | 36 | // This can be a local because we're storing a reference 37 | // directly inside the panels. 38 | Model lModel = new Model(); 39 | 40 | add(new SliderPanel(lModel)); // Top component 41 | add(new IntegerPanel(lModel)); // Middle component 42 | add(new IntegerPanel(lModel)); // Middle component 43 | add(new TextPanel(lModel)); // Bottom component 44 | setDefaultCloseOperation(EXIT_ON_CLOSE); // Activates the close button. 45 | pack(); // Computes optimal size of the window 46 | setLocationRelativeTo(null); // Sticks the window in the middle of the screen 47 | setVisible(true); 48 | } 49 | 50 | public static void main(String[] args) 51 | { 52 | new LuckyNumber(); 53 | } 54 | } 55 | 56 | @SuppressWarnings("serial") 57 | class SliderPanel extends JPanel implements Observer, ChangeListener 58 | { 59 | private JSlider aSlider = new JSlider(); 60 | private Model aModel; 61 | 62 | public SliderPanel(Model pModel) 63 | { 64 | aModel = pModel; 65 | aModel.addObserver(this); 66 | aSlider.setMinimum(0); 67 | aSlider.setMaximum(10); 68 | aSlider.setPaintTicks(true); 69 | aSlider.setMajorTickSpacing(1); 70 | aSlider.setSnapToTicks(true); 71 | aSlider.setValue(aModel.getNumber()); 72 | aSlider.addChangeListener(this); 73 | add(aSlider); 74 | } 75 | 76 | @Override 77 | public void stateChanged(ChangeEvent e) 78 | { 79 | aModel.setNumber(aSlider.getValue()); 80 | } 81 | 82 | @Override 83 | public void newNumber(int pNumber) 84 | { 85 | aSlider.setValue(pNumber); 86 | } 87 | } 88 | 89 | @SuppressWarnings("serial") 90 | class IntegerPanel extends JPanel implements Observer//, ActionListener 91 | { 92 | private JTextField aText = new JTextField(20); 93 | private Model aModel; 94 | 95 | public IntegerPanel(Model pModel) 96 | { 97 | aModel = pModel; 98 | aModel.addObserver(this); 99 | aText.setText(new Integer(aModel.getNumber()).toString()); 100 | aText.addActionListener( new ActionListener() 101 | { 102 | 103 | @Override 104 | public void actionPerformed(ActionEvent arg0) 105 | { 106 | int lInteger = 0; 107 | try 108 | { 109 | lInteger = Integer.parseInt(aText.getText()); 110 | } 111 | catch(NumberFormatException pException ) 112 | { 113 | // Just ignore. We'll use 0 instead. 114 | } 115 | aModel.setNumber(lInteger); 116 | } 117 | }); 118 | add(aText); 119 | } 120 | 121 | // @Override 122 | // public void actionPerformed(ActionEvent e) 123 | // { 124 | // int lInteger = 0; 125 | // try 126 | // { 127 | // lInteger = Integer.parseInt(aText.getText()); 128 | // } 129 | // catch(NumberFormatException pException ) 130 | // { 131 | // // Just ignore. We'll use 0 instead. 132 | // } 133 | // aModel.setNumber(lInteger); 134 | // } 135 | 136 | @Override 137 | public void newNumber(int pNumber) 138 | { 139 | aText.setText(new Integer(pNumber).toString()); 140 | 141 | } 142 | } 143 | 144 | @SuppressWarnings("serial") 145 | class TextPanel extends JPanel implements Observer, ActionListener 146 | { 147 | private JTextField aText = new JTextField(20); 148 | private Model aModel; 149 | 150 | private static final String[] LABELS = {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"}; 151 | 152 | public TextPanel(Model pModel) 153 | { 154 | aModel = pModel; 155 | aModel.addObserver(this); 156 | aText.setText(LABELS[aModel.getNumber()]); 157 | aText.addActionListener(this); 158 | add(aText); 159 | } 160 | 161 | @Override 162 | public void actionPerformed(ActionEvent e) 163 | { 164 | int lIndex = 0; 165 | for( int i = 0; i < LABELS.length; i++) 166 | { 167 | if(aText.getText().equalsIgnoreCase(LABELS[i])) 168 | { 169 | lIndex = i; 170 | break; 171 | } 172 | } 173 | aModel.setNumber(lIndex); 174 | } 175 | 176 | @Override 177 | public void newNumber(int pNumber) 178 | { 179 | aText.setText(LABELS[pNumber]); 180 | } 181 | } 182 | 183 | class Model 184 | { 185 | private ArrayList aObservers = new ArrayList(); 186 | private int aNumber = 5; 187 | 188 | public void addObserver(Observer pObserver) 189 | { 190 | aObservers.add(pObserver); 191 | } 192 | 193 | private void notifyObservers() 194 | { 195 | for(Observer observer : aObservers) 196 | { 197 | observer.newNumber(aNumber); 198 | } 199 | } 200 | 201 | public void setNumber(int pNumber) 202 | { 203 | if( pNumber < 0 ) 204 | { 205 | aNumber = 0; 206 | } 207 | else if( pNumber > 10 ) 208 | { 209 | aNumber = 10; 210 | } 211 | else 212 | { 213 | aNumber = pNumber; 214 | } 215 | notifyObservers(); 216 | } 217 | 218 | public int getNumber() 219 | { 220 | return aNumber; 221 | } 222 | } 223 | 224 | interface Observer 225 | { 226 | void newNumber(int pNumber); 227 | } -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/DefaultVisitor.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * Default implementation of the structure traversal in the Visitor. 7 | * Here we can get rid of double dispatch and call visit directly from 8 | * within visit, but only because of special circumstances: we know the 9 | * specific concrete types of elements we are visiting. This is not always 10 | * the case, especially in object graphs generated from recursive structures. 11 | */ 12 | public class DefaultVisitor implements Visitor 13 | { 14 | @Override 15 | public void visitUniversity(University pUniversity) 16 | { 17 | for( Iterator i = pUniversity.getFaculties(); i.hasNext(); ) 18 | { 19 | i.next().accept(this); 20 | } 21 | } 22 | 23 | @Override 24 | public void visitFaculty(Faculty pFaculty) 25 | { 26 | for( Iterator i = pFaculty.getDepartments(); i.hasNext(); ) 27 | { 28 | i.next().accept(this); 29 | } 30 | 31 | for( Iterator i = pFaculty.getCommittees(); i.hasNext(); ) 32 | { 33 | i.next().accept(this); 34 | } 35 | } 36 | 37 | @Override 38 | public void visitDepartment(Department pDepartment) 39 | { 40 | for( Iterator i = pDepartment.getCommittees(); i.hasNext(); ) 41 | { 42 | i.next().accept(this); 43 | } 44 | } 45 | 46 | @Override 47 | public void visitCommittee(Committee pCommittee) 48 | { 49 | 50 | for( Iterator i = pCommittee.getCommittees(); i.hasNext(); ) 51 | { 52 | i.next().accept(this); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/IVisitable.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | public interface IVisitable 4 | { 5 | void accept(Visitor pVisitor); 6 | } 7 | -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/McGill.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | public class McGill 4 | { 5 | public static void main(String[] args) 6 | { 7 | 8 | University mcGill = new University("McGill"); 9 | 10 | Faculty science = new Faculty("Science"); 11 | Faculty arts = new Faculty("Arts"); 12 | 13 | Department cs = new Department("Computer Science"); 14 | Department physics = new Department("Physics"); 15 | Department history = new Department("History and Classical Studies"); 16 | 17 | Committee lAca = new Committee("Academic"); 18 | Committee lSco = new Committee("Scholarship"); 19 | Committee lMsc = new Committee("MSc"); 20 | Committee lWeb = new Committee("Web"); 21 | Committee lStudents = new Committee("Students"); 22 | 23 | mcGill.addFaculty(science); 24 | mcGill.addFaculty(arts); 25 | science.addDepartment(cs); 26 | science.addDepartment(physics); 27 | arts.addDepartment(history); 28 | 29 | science.addCommittee(lAca); 30 | science.addCommittee(lSco); 31 | arts.addCommittee(lStudents); 32 | 33 | cs.addCommittee(lMsc); 34 | lMsc.addCommittee(lWeb); 35 | 36 | SearchVisitor searcher = new SearchVisitor("Foo"); 37 | mcGill.accept(searcher); 38 | OrgNode result = searcher.getResult(); 39 | System.out.println(result.getName()); 40 | // mcGill.accept(new PrintVisitor()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/OrgNode.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | public abstract class OrgNode implements IVisitable 8 | { 9 | private String aName = ""; 10 | 11 | public String getName() { return aName; } 12 | public OrgNode( String pName ) { aName = pName; } 13 | public String toString() { return getName(); } 14 | public boolean isNull() { return false;} 15 | } 16 | 17 | class NullOrgNode extends OrgNode 18 | { 19 | 20 | public NullOrgNode() 21 | { 22 | super("Null"); 23 | } 24 | 25 | 26 | @Override 27 | public void accept(Visitor pVisitor) 28 | { 29 | 30 | } 31 | 32 | @Override 33 | public boolean isNull() 34 | { 35 | return true; 36 | } 37 | 38 | } 39 | 40 | class University extends OrgNode 41 | { 42 | private final List aFaculties = new ArrayList(); 43 | 44 | public University(String pName) { super(pName); } 45 | public void addFaculty(Faculty pFaculty) { aFaculties.add(pFaculty); } 46 | public Iterator getFaculties() { return aFaculties.iterator(); } 47 | @Override 48 | public void accept(Visitor pVisitor) 49 | { 50 | pVisitor.visitUniversity(this); 51 | // for( Faculty f : aFaculties ) 52 | // { 53 | // f.accept(pVisitor); 54 | // } 55 | 56 | } 57 | } 58 | 59 | class Faculty extends OrgNode 60 | { 61 | private final List aDepts = new ArrayList(); 62 | private final List aCommittees = new ArrayList(); 63 | 64 | public Faculty(String pName) { super(pName); } 65 | public void addDepartment(Department pDepartment) { aDepts.add(pDepartment); } 66 | public void addCommittee(Committee pCommittee) { aCommittees.add(pCommittee); } 67 | public Iterator getDepartments() { return aDepts.iterator(); } 68 | public Iterator getCommittees() { return aCommittees.iterator(); } 69 | 70 | @Override 71 | public void accept(Visitor pVisitor) 72 | { 73 | pVisitor.visitFaculty(this); 74 | // for(Department d : aDepts ) 75 | // { 76 | // d.accept(pVisitor); 77 | // } 78 | // for(Committee c : aCommittees) 79 | // { 80 | // c.accept(pVisitor); 81 | // } 82 | 83 | } 84 | } 85 | 86 | class Department extends OrgNode 87 | { 88 | private final List aCommittees = new ArrayList(); 89 | 90 | public Department(String pName) { super(pName); } 91 | public void addCommittee(Committee pCommittee) { aCommittees.add(pCommittee); } 92 | public Iterator getCommittees() { return aCommittees.iterator(); } 93 | @Override 94 | public void accept(Visitor pVisitor) 95 | { 96 | pVisitor.visitDepartment(this); 97 | // for(Committee c : aCommittees) 98 | // { 99 | // c.accept(pVisitor); 100 | // } 101 | 102 | } 103 | } 104 | 105 | class Committee extends OrgNode 106 | { 107 | private final List aCommittees = new ArrayList(); 108 | 109 | public Committee(String pName) { super(pName); } 110 | public void addCommittee(Committee pCommittee) { aCommittees.add(pCommittee); } 111 | public Iterator getCommittees() { return aCommittees.iterator(); } 112 | 113 | @Override 114 | public void accept(Visitor pVisitor) 115 | { 116 | pVisitor.visitCommittee(this); 117 | // for(Committee c : aCommittees) 118 | // { 119 | // c.accept(pVisitor); 120 | // } 121 | } 122 | } -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/PrintVisitor.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | import java.util.Iterator; 4 | 5 | public class PrintVisitor implements Visitor 6 | { 7 | 8 | private final StringBuilder aPrefix = new StringBuilder(); 9 | private static final String TAB = " "; 10 | 11 | private void increaseTab() 12 | { 13 | aPrefix.append(TAB); 14 | } 15 | 16 | private void decreaseTab() 17 | { 18 | aPrefix.delete(aPrefix.length()-TAB.length(), aPrefix.length()); 19 | } 20 | 21 | private String tab() 22 | { 23 | return aPrefix.toString(); 24 | } 25 | 26 | @Override 27 | public void visitUniversity(University pUniversity) 28 | { 29 | System.out.println(pUniversity.getName()); 30 | increaseTab(); 31 | for( Iterator i = pUniversity.getFaculties(); i.hasNext(); ) 32 | { 33 | i.next().accept(this); 34 | } 35 | decreaseTab(); 36 | 37 | } 38 | 39 | @Override 40 | public void visitFaculty(Faculty pFaculty) 41 | { 42 | System.out.println(tab() + pFaculty.getName()); 43 | increaseTab(); 44 | for( Iterator i = pFaculty.getDepartments(); i.hasNext(); ) 45 | { 46 | i.next().accept(this); 47 | } 48 | 49 | for( Iterator i = pFaculty.getCommittees(); i.hasNext(); ) 50 | { 51 | i.next().accept(this); 52 | } 53 | decreaseTab(); 54 | 55 | 56 | } 57 | 58 | @Override 59 | public void visitDepartment(Department pDepartment) 60 | { 61 | System.out.println(tab() + pDepartment.getName()); 62 | increaseTab(); 63 | for( Iterator i = pDepartment.getCommittees(); i.hasNext(); ) 64 | { 65 | i.next().accept(this); 66 | } 67 | decreaseTab(); 68 | } 69 | 70 | @Override 71 | public void visitCommittee(Committee pCommittee) 72 | { 73 | System.out.println(tab() + "C: " + pCommittee.getName()); 74 | increaseTab(); 75 | for( Iterator i = pCommittee.getCommittees(); i.hasNext(); ) 76 | { 77 | i.next().accept(this); 78 | } 79 | decreaseTab(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/SearchVisitor.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | public class SearchVisitor extends DefaultVisitor 4 | { 5 | private final String aQuery; 6 | private OrgNode aResult = new NullOrgNode(); 7 | 8 | public SearchVisitor(String pQuery) 9 | { 10 | aQuery = pQuery; 11 | } 12 | 13 | OrgNode getResult() {return aResult; } 14 | 15 | 16 | @Override 17 | public void visitCommittee(Committee pCommittee) 18 | { 19 | if( pCommittee.getName().equals(aQuery)) 20 | { 21 | aResult = pCommittee; 22 | } 23 | super.visitCommittee(pCommittee); 24 | } 25 | 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /modules/artifacts/module-08/module08/Visitor.java: -------------------------------------------------------------------------------- 1 | package module08; 2 | 3 | public interface Visitor 4 | { 5 | void visitUniversity(University pUniversity); 6 | void visitFaculty(Faculty pFaculty); 7 | void visitDepartment(Department pDepartment); 8 | void visitCommittee(Committee pCommittee); 9 | } 10 | -------------------------------------------------------------------------------- /modules/artifacts/module-09/module9/DistributedComputation.java: -------------------------------------------------------------------------------- 1 | package module9; 2 | 3 | /** 4 | * Illustrates the motivation for and use of Thread.join() 5 | */ 6 | public class DistributedComputation 7 | { 8 | public static void main(String[] args) 9 | { 10 | NumberAdder adder1 = new NumberAdder(1000000); 11 | NumberAdder adder2 = new NumberAdder(2000000); 12 | NumberAdder adder3 = new NumberAdder(3000000); 13 | NumberAdder adder4 = new NumberAdder(4000000); 14 | 15 | // Worker threads 16 | adder1.start(); 17 | adder2.start(); 18 | adder3.start(); 19 | adder4.start(); 20 | 21 | // Thread that aggregates and displays the result 22 | new Thread(new Runnable(){ 23 | 24 | @Override 25 | public void run() 26 | { 27 | try 28 | { 29 | adder1.join(); 30 | adder2.join(); 31 | adder3.join(); 32 | adder4.join(); 33 | // Warning: not synchronized! 34 | System.out.println(adder1.getResult() + adder2.getResult() + 35 | adder3.getResult() + adder4.getResult()); 36 | } 37 | catch (InterruptedException e) 38 | { 39 | return; 40 | } 41 | 42 | }}).start(); 43 | } 44 | } 45 | 46 | /** 47 | * Sum positive integer sequence up to and 48 | * including N using a brute force/naive 49 | * technique. 50 | */ 51 | class NumberAdder extends Thread 52 | { 53 | private final int aLast; 54 | private int aSum = 0; 55 | 56 | public NumberAdder(int pLast) 57 | { 58 | aLast = pLast; 59 | } 60 | 61 | @Override 62 | public void run() 63 | { 64 | aSum = 0; 65 | for( int i = 0; i <= aLast; i++ ) 66 | { 67 | aSum += i; 68 | } 69 | } 70 | 71 | public int getResult() 72 | { 73 | return aSum; 74 | } 75 | } -------------------------------------------------------------------------------- /modules/artifacts/module-09/module9/IndecisivePerson.java: -------------------------------------------------------------------------------- 1 | package module9; 2 | 3 | /** 4 | * Basic demonstration of thread creation in Java. 5 | * 6 | */ 7 | public class IndecisivePerson 8 | { 9 | public static void main(String[] args) 10 | { 11 | Thread t1 = new Thread( new Runnable() { 12 | 13 | @Override 14 | public void run() 15 | { 16 | while(!Thread.interrupted()) 17 | { 18 | System.out.println("Maybe"); 19 | try 20 | { 21 | Thread.sleep(0); 22 | } 23 | catch (InterruptedException e) 24 | { 25 | return; 26 | } 27 | } 28 | 29 | }} 30 | 31 | ); 32 | 33 | Thread t2 = new Thread( new Runnable() { 34 | 35 | @Override 36 | public void run() 37 | { 38 | while(!Thread.interrupted()) 39 | { 40 | System.out.println("Not"); 41 | try 42 | { 43 | Thread.sleep(0); 44 | } 45 | catch (InterruptedException e) 46 | { 47 | return; 48 | } 49 | } 50 | 51 | }} 52 | 53 | ); 54 | t1.start(); 55 | t2.start(); 56 | new Thread(new Runnable(){ 57 | 58 | @Override 59 | public void run() 60 | { 61 | try 62 | { 63 | Thread.sleep(2000); 64 | } 65 | catch (InterruptedException e) 66 | { 67 | return; 68 | } 69 | t1.interrupt(); 70 | t2.interrupt(); 71 | }}).start(); 72 | } 73 | } 74 | 75 | class MyThread implements Runnable 76 | { 77 | private final String aMessage; 78 | 79 | public MyThread(String pMessage) 80 | { 81 | aMessage = pMessage; 82 | } 83 | 84 | @Override 85 | public void run() 86 | { 87 | while(true) 88 | { 89 | System.out.println(aMessage); 90 | } 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /modules/artifacts/module-09/module9/JoiningHands.java: -------------------------------------------------------------------------------- 1 | package module9; 2 | 3 | /** 4 | * Demonstrates the use of join and the effect of interrupting 5 | * either the waiting or working thread. 6 | * 7 | */ 8 | public class JoiningHands 9 | { 10 | public static void main(String[] args) 11 | { 12 | final Thread t1 = new Thread( new Runnable() 13 | { 14 | public void run() 15 | { 16 | try 17 | { 18 | Thread.sleep(5000); 19 | System.out.println("T1 completed normally"); 20 | } 21 | catch(InterruptedException e) 22 | { 23 | System.out.println("T1 interrupted"); 24 | } 25 | } 26 | }); 27 | final Thread t2 = new Thread( new Runnable() 28 | { 29 | public void run() 30 | { 31 | try 32 | { 33 | t1.join(); 34 | System.out.println("T2 completed normally"); 35 | } 36 | catch(InterruptedException e) 37 | { 38 | System.out.println("T2 interrupted"); 39 | } 40 | } 41 | }); 42 | t1.start(); 43 | t2.start(); 44 | // t1.interrupt(); // Uncomment and t2 will synchronize on t1's abnormally quick termination 45 | t2.interrupt(); // Uncomment and t2 will be interrupted, but t1 will remain oblivious and terminate normally 46 | } 47 | } -------------------------------------------------------------------------------- /modules/artifacts/module-09/module9/PrintLotsOfNumbers.java: -------------------------------------------------------------------------------- 1 | package module9; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | 7 | import javax.swing.JButton; 8 | import javax.swing.JFrame; 9 | import javax.swing.SwingUtilities; 10 | 11 | @SuppressWarnings("serial") 12 | public class PrintLotsOfNumbers extends JFrame 13 | { 14 | public PrintLotsOfNumbers() 15 | { 16 | final JButton button = new JButton("Go"); 17 | button.setPreferredSize(new Dimension(50,25)); 18 | add(button); 19 | 20 | button.addActionListener(new ActionListener() 21 | { 22 | public void actionPerformed(ActionEvent arg0) 23 | { 24 | button.setText("Processing..."); 25 | new Thread(new Runnable() 26 | { 27 | public void run() 28 | { 29 | for( int i = 0; i < 500000; i++) 30 | { 31 | System.out.println(i); 32 | } 33 | 34 | SwingUtilities.invokeLater(new Runnable() 35 | { 36 | public void run() 37 | { 38 | button.setText("Foowawa!"); 39 | } 40 | }); 41 | for( int i = 0; i < 500000; i++) 42 | { 43 | System.out.println(i); 44 | } 45 | SwingUtilities.invokeLater(new Runnable() 46 | { 47 | public void run() 48 | { 49 | button.setText("Done!"); 50 | } 51 | }); 52 | 53 | } 54 | }).start(); 55 | } 56 | }); 57 | 58 | setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); 59 | setLocationRelativeTo(null); 60 | pack(); 61 | setVisible( true ); 62 | } 63 | 64 | public static void main(String[] args) 65 | { 66 | new PrintLotsOfNumbers(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /modules/artifacts/module-09/module9/RandomLoop.java: -------------------------------------------------------------------------------- 1 | package module9; 2 | 3 | import java.io.FileWriter; 4 | import java.io.PrintWriter; 5 | import java.util.Random; 6 | 7 | /** 8 | * If you get radically different results on your machine (see slide) 9 | * please post them on the Lectures forum. 10 | */ 11 | public class RandomLoop implements Runnable 12 | { 13 | private static final int MAX_ITERATIONS = 1000000; 14 | 15 | @Override 16 | public void run() 17 | { 18 | for( int i = 0; i < new Random().nextInt(MAX_ITERATIONS); i++ ) 19 | { 20 | System.out.println("foo"); 21 | } 22 | } 23 | 24 | public static void main(String[] args) 25 | { 26 | Thread counter = new Thread(new Runnable() 27 | { 28 | @Override 29 | public void run() 30 | { 31 | try 32 | { 33 | PrintWriter out = new PrintWriter(new FileWriter("C:\\temp\\data.csv")); 34 | int nbOfThreads = 0; 35 | while(true) 36 | { 37 | int current = Thread.activeCount(); 38 | if( current != nbOfThreads ) 39 | { 40 | nbOfThreads = current; 41 | out.println(System.currentTimeMillis() + "," + current); 42 | } 43 | if(current == 2) 44 | { 45 | out.close(); 46 | break; 47 | } 48 | } 49 | } 50 | catch(Exception e){} 51 | } 52 | }); 53 | counter.start(); 54 | for( int i = 0; i < 1000; i++ ) 55 | { 56 | new Thread(new RandomLoop()).start(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/DeserializationException.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | @SuppressWarnings("serial") 4 | public class DeserializationException extends RuntimeException 5 | { 6 | public DeserializationException() 7 | { 8 | super(); 9 | } 10 | 11 | public DeserializationException(String message, Throwable cause, boolean enableSuppression, 12 | boolean writableStackTrace) 13 | { 14 | super(message, cause, enableSuppression, writableStackTrace); 15 | } 16 | 17 | public DeserializationException(String message, Throwable cause) 18 | { 19 | super(message, cause); 20 | } 21 | 22 | public DeserializationException(String message) 23 | { 24 | super(message); 25 | } 26 | 27 | public DeserializationException(Throwable cause) 28 | { 29 | super(cause); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product1.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | /** 4 | * Sample "flat" data structure that can easily be serialized 5 | * in Comma-separated value (CSV) format, or similarly 6 | * using other conventions. 7 | */ 8 | public class Product1 9 | { 10 | private String aName; 11 | private int aId; 12 | 13 | public Product1(String pName, int pId) 14 | { 15 | aName = pName; 16 | aId = pId; 17 | } 18 | 19 | public String getName() 20 | { 21 | return aName; 22 | } 23 | 24 | public int getId() 25 | { 26 | return aId; 27 | } 28 | 29 | @Override 30 | public String toString() 31 | { 32 | return aId + "," + aName; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product1a.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import org.apache.commons.csv.CSVFormat; 7 | import org.apache.commons.csv.CSVParser; 8 | import org.apache.commons.csv.CSVRecord; 9 | 10 | /** 11 | * Sample "flat" data structure that can easily be serialized 12 | * in Comma-separated value (CSV) format, or similarly 13 | * using other conventions, more robust and better encapsulated than 14 | * product1. 15 | */ 16 | public class Product1a 17 | { 18 | private String aName; 19 | private int aId; 20 | 21 | public Product1a(String pName, int pId) 22 | { 23 | aName = pName; 24 | aId = pId; 25 | } 26 | 27 | public String getName() 28 | { 29 | return aName; 30 | } 31 | 32 | public int getId() 33 | { 34 | return aId; 35 | } 36 | 37 | @Override 38 | public String toString() 39 | { 40 | return aId + "," + aName; 41 | } 42 | 43 | public String serialize() 44 | { 45 | return "\"" + aName + "\"," + aId; 46 | } 47 | 48 | public Product1a(String pSerialized) 49 | { 50 | try 51 | { 52 | CSVParser parser = CSVParser.parse(pSerialized, CSVFormat.DEFAULT); 53 | List records = parser.getRecords(); 54 | if( records.size() != 1 ) 55 | { 56 | throw new DeserializationException(); 57 | } 58 | CSVRecord record = records.get(0); 59 | if(record.size() != 2 ) 60 | { 61 | throw new DeserializationException(); 62 | } 63 | aName = record.get(0); 64 | aId = Integer.parseInt(record.get(1)); 65 | } 66 | catch(IOException | NumberFormatException e) 67 | { 68 | throw new DeserializationException(e); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product2.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.util.HashMap; 4 | import java.util.Iterator; 5 | 6 | import org.json.JSONObject; 7 | 8 | /** 9 | * Sample hierarchical structure.. 10 | */ 11 | public class Product2 12 | { 13 | private String aName; 14 | private int aId; 15 | private HashMap aProperties = new HashMap<>(); 16 | 17 | public Product2(String pName, int pId) 18 | { 19 | aName = pName; 20 | aId = pId; 21 | } 22 | 23 | public void setProperty(String pKey, String pValue) 24 | { 25 | aProperties.put(pKey, pValue); 26 | } 27 | 28 | public String getProperty(String pKey) 29 | { 30 | return aProperties.get(pKey); 31 | } 32 | 33 | public Iterator getPropertyNames() 34 | { 35 | return aProperties.keySet().iterator(); 36 | } 37 | 38 | @Override 39 | public String toString() 40 | { 41 | return aId + "," + aName + " " + aProperties; 42 | } 43 | 44 | public JSONObject toJSONObject() 45 | { 46 | // First a properties object where keys become fields 47 | JSONObject properties = new JSONObject(); 48 | for( String key : aProperties.keySet() ) 49 | { 50 | properties.put(key, aProperties.get(key)); 51 | } 52 | 53 | // Then construct the actual product object 54 | JSONObject product = new JSONObject(); 55 | product.put("name", aName); 56 | product.put("id", aId); 57 | product.put("properties", properties); 58 | 59 | return product; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product2a.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.util.HashMap; 4 | import java.util.Iterator; 5 | 6 | import org.json.JSONObject; 7 | 8 | /** 9 | * Sample hierarchical structure, initially without links, 10 | * but with aRelated added in, demonstrates the weakness 11 | * of JSON serialization for graphs. 12 | */ 13 | public class Product2a 14 | { 15 | private String aName; 16 | private int aId; 17 | private HashMap aProperties = new HashMap<>(); 18 | private Product2a aRelated; 19 | 20 | public Product2a(String pName, int pId) 21 | { 22 | aName = pName; 23 | aId = pId; 24 | } 25 | 26 | public void setRelated(Product2a p) 27 | { 28 | aRelated = p; 29 | } 30 | 31 | public void setProperty(String pKey, String pValue) 32 | { 33 | aProperties.put(pKey, pValue); 34 | } 35 | 36 | public String getProperties(String pKey) 37 | { 38 | return aProperties.get(pKey); 39 | } 40 | 41 | public Iterator getPropertyNames() 42 | { 43 | return aProperties.keySet().iterator(); 44 | } 45 | 46 | public JSONObject toJSONObject() 47 | { 48 | // First a properties object where keys become fields 49 | JSONObject properties = new JSONObject(); 50 | for( String key : aProperties.keySet() ) 51 | { 52 | properties.put(key, aProperties.get(key)); 53 | } 54 | 55 | // Then construct the actual product object 56 | JSONObject product = new JSONObject(); 57 | product.put("name", aName); 58 | product.put("id", aId); 59 | product.put("properties", properties); 60 | if( aRelated != null ) 61 | { 62 | product.put("related", aRelated.toJSONObject()); 63 | } 64 | 65 | return product; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product3.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.util.HashMap; 4 | import java.util.Iterator; 5 | 6 | /** 7 | * Product object in the JavaBeans framework. 8 | */ 9 | public class Product3 10 | { 11 | private String aName = "default"; 12 | private int aId = -1; 13 | private HashMap aProperties = new HashMap<>(); 14 | private Product3 aRelated; 15 | 16 | public Product3() 17 | {} 18 | 19 | public Product3(String pName, int pId) 20 | { 21 | aName = pName; 22 | aId = pId; 23 | } 24 | 25 | public String getName() 26 | { 27 | return aName; 28 | } 29 | 30 | public void setName(String pName) 31 | { 32 | aName = pName; 33 | } 34 | 35 | public int getId() 36 | { 37 | return aId; 38 | } 39 | 40 | public void setId(int pId) 41 | { 42 | aId = pId; 43 | } 44 | 45 | public Product3 getRelated() 46 | { 47 | return aRelated; 48 | } 49 | 50 | public void setRelated(Product3 pRelated) 51 | { 52 | aRelated = pRelated; 53 | } 54 | 55 | public void setProperty(String pKey, String pValue) 56 | { 57 | aProperties.put(pKey, pValue); 58 | } 59 | 60 | public String getProperty(String pKey) 61 | { 62 | return aProperties.get(pKey); 63 | } 64 | 65 | public Iterator getPropertyNames() 66 | { 67 | return aProperties.keySet().iterator(); 68 | } 69 | 70 | @Override 71 | public String toString() 72 | { 73 | return aId + "," + aName + " " + aProperties; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/Product4.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | 7 | /** 8 | */ 9 | public class Product4 implements Serializable 10 | { 11 | private String aName; 12 | private int aId; 13 | private transient HashMap aProperties = new HashMap<>(); 14 | private Product4 aRelated; 15 | 16 | public Product4(String pName, int pId) 17 | { 18 | aName = pName; 19 | aId = pId; 20 | } 21 | 22 | public void setRelated(Product4 p) 23 | { 24 | aRelated = p; 25 | } 26 | 27 | public void setProperty(String pKey, String pValue) 28 | { 29 | aProperties.put(pKey, pValue); 30 | } 31 | 32 | public String getProperties(String pKey) 33 | { 34 | return aProperties.get(pKey); 35 | } 36 | 37 | public Iterator getPropertyNames() 38 | { 39 | return aProperties.keySet().iterator(); 40 | } 41 | 42 | @Override 43 | public String toString() 44 | { 45 | return aId + "," + aName + " " + aProperties; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleBinary1.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | 9 | public class SerializationExampleBinary1 10 | { 11 | public static void main(String[] args) throws Exception 12 | { 13 | Product4 product1 = new Product4("TV", 1); 14 | Product4 product2 = new Product4("Phone", 2); 15 | Product4 product3 = new Product4("Radio", 3); 16 | 17 | product1.setRelated(product2); 18 | product2.setRelated(product1); 19 | 20 | product1.setProperty("screen", "bw"); 21 | product1.setProperty("weight", "950"); 22 | product2.setProperty("os", "Win"); 23 | product3.setProperty("default", "CBC"); 24 | 25 | Product4[] products = {product1, product2, product3}; 26 | write(products); 27 | 28 | Product4[] readProducts = read(); 29 | 30 | print(readProducts); 31 | } 32 | 33 | private static void write(Product4[] pProducts) throws IOException 34 | { 35 | try( ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("products.dat"))) 36 | { 37 | out.writeObject(pProducts); 38 | } 39 | } 40 | 41 | private static Product4[] read() throws IOException, ClassNotFoundException 42 | { 43 | try( ObjectInputStream in = new ObjectInputStream(new FileInputStream("products.dat"))) 44 | { 45 | return (Product4[])in.readObject(); 46 | } 47 | } 48 | 49 | private static void print(Product4[] pProducts) 50 | { 51 | for( Product4 product : pProducts ) 52 | { 53 | System.out.println(product); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleCSV1.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class SerializationExampleCSV1 12 | { 13 | private static final String PRODUCT_FILE_NAME = "products.csv"; 14 | 15 | public static void main(String[] args) throws IOException 16 | { 17 | Product1[] products = {new Product1("Pen, and paper", 1), 18 | new Product1("Mug", 2), 19 | new Product1("Eraser", 3)}; 20 | 21 | write(products); 22 | Product1[] products2 = read(); 23 | print(products2); 24 | } 25 | 26 | private static void write(Product1[] pProducts) throws IOException 27 | { 28 | try( PrintWriter out = new PrintWriter( new FileWriter(PRODUCT_FILE_NAME))) 29 | { 30 | for( Product1 product : pProducts ) 31 | { 32 | out.println(product.getId() + "," + product.getName()); 33 | } 34 | } 35 | } 36 | 37 | private static Product1[] read() throws IOException 38 | { 39 | List products = new ArrayList<>(); 40 | try(BufferedReader in = new BufferedReader(new FileReader(PRODUCT_FILE_NAME))) 41 | { 42 | String line = in.readLine(); 43 | while( line != null) 44 | { 45 | String[] tokens = line.split(","); 46 | products.add(new Product1(tokens[1],Integer.parseInt(tokens[0]))); 47 | line = in.readLine(); 48 | } 49 | return products.toArray(new Product1[products.size()]); 50 | } 51 | } 52 | 53 | private static void print(Product1[] pProducts) 54 | { 55 | for( Product1 product : pProducts ) 56 | { 57 | System.out.println(product); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleCSV2.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class SerializationExampleCSV2 12 | { 13 | private static final String PRODUCT_FILE_NAME = "products.csv"; 14 | 15 | public static void main(String[] args) throws IOException 16 | { 17 | Product1a[] products = {new Product1a("Pen, and etrase", 1), 18 | new Product1a("Mug", 2), 19 | new Product1a("Eraser", 3)}; 20 | 21 | write(products); 22 | Product1a[] products2 = read(); 23 | print(products2); 24 | } 25 | 26 | private static void write(Product1a[] pProducts) throws IOException 27 | { 28 | try( PrintWriter out = new PrintWriter( new FileWriter(PRODUCT_FILE_NAME))) 29 | { 30 | for( Product1a product : pProducts ) 31 | { 32 | out.println(product.serialize()); 33 | } 34 | } 35 | } 36 | 37 | private static Product1a[] read() throws IOException 38 | { 39 | List products = new ArrayList<>(); 40 | try(BufferedReader in = new BufferedReader(new FileReader(PRODUCT_FILE_NAME))) 41 | { 42 | String line = in.readLine(); 43 | while( line != null) 44 | { 45 | products.add(new Product1a(line)); 46 | line = in.readLine(); 47 | } 48 | return products.toArray(new Product1a[products.size()]); 49 | } 50 | } 51 | 52 | private static void print(Product1a[] pProducts) 53 | { 54 | for( Product1a product : pProducts ) 55 | { 56 | System.out.println(product); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleCSVa.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileReader; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.io.PrintWriter; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class SerializationExampleCSVa 12 | { 13 | private static final String PRODUCT_FILE_NAME = "products.csv"; 14 | 15 | public static void main(String[] args) throws IOException 16 | { 17 | Product1[] products = {new Product1("Pen", 1), 18 | new Product1("Mug", 2), 19 | new Product1("Eraser", 3)}; 20 | 21 | write(products); 22 | Product1[] products2 = read(); 23 | print(products2); 24 | } 25 | 26 | private static void write(Product1[] pProducts) throws IOException 27 | { 28 | try( PrintWriter out = new PrintWriter( new FileWriter(PRODUCT_FILE_NAME))) 29 | { 30 | for( Product1 product : pProducts ) 31 | { 32 | out.println(product.getId() + "," + product.getName()); 33 | } 34 | } 35 | } 36 | 37 | private static Product1[] read() throws IOException 38 | { 39 | List products = new ArrayList<>(); 40 | try(BufferedReader in = new BufferedReader(new FileReader(PRODUCT_FILE_NAME))) 41 | { 42 | String line = in.readLine(); 43 | while( line != null) 44 | { 45 | String[] tokens = line.split(","); 46 | products.add(new Product1(tokens[1],Integer.parseInt(tokens[0]))); 47 | line = in.readLine(); 48 | } 49 | return products.toArray(new Product1[products.size()]); 50 | } 51 | } 52 | 53 | private static void print(Product1[] pProducts) 54 | { 55 | for( Product1 product : pProducts ) 56 | { 57 | System.out.println(product); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleJSON1.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | 7 | import org.json.JSONArray; 8 | 9 | import com.google.gson.Gson; 10 | 11 | public class SerializationExampleJSON1 12 | { 13 | public static void main(String[] args) throws IOException 14 | { 15 | Product2 product1 = new Product2("TV", 1); 16 | Product2 product2 = new Product2("Phone", 2); 17 | Product2 product3 = new Product2("Radio", 3); 18 | 19 | product1.setProperty("screen", "bw"); 20 | product1.setProperty("weight", "950"); 21 | product2.setProperty("os", "Win"); 22 | product3.setProperty("default", "CBC"); 23 | 24 | Product2[] products = {product1, product2, product3}; 25 | 26 | writeGSON(products); 27 | } 28 | 29 | private static void write(Product2[] pProducts) throws IOException 30 | { 31 | JSONArray products = new JSONArray(); 32 | for( Product2 product : pProducts ) 33 | { 34 | products.put(product.toJSONObject()); 35 | } 36 | try( PrintWriter out = new PrintWriter(new FileWriter("products.json"))) 37 | { 38 | products.write(out); 39 | } 40 | } 41 | 42 | private static void writeGSON(Product2[] pProducts) throws IOException 43 | { 44 | Gson gson = new Gson(); 45 | try( PrintWriter out = new PrintWriter(new FileWriter("products-g.json"))) 46 | { 47 | out.write(gson.toJson(pProducts)); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleJSON2.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | 7 | import org.json.JSONArray; 8 | 9 | import com.google.gson.Gson; 10 | 11 | public class SerializationExampleJSON2 12 | { 13 | public static void main(String[] args) throws Exception 14 | { 15 | Product2a product1 = new Product2a("TV", 1); 16 | Product2a product2 = new Product2a("Phone", 2); 17 | Product2a product3 = new Product2a("Radio", 3); 18 | 19 | product1.setProperty("screen", "bw"); 20 | product1.setProperty("weight", "950"); 21 | product2.setProperty("os", "Win"); 22 | product3.setProperty("default", "CBC"); 23 | 24 | product1.setRelated(product3); 25 | product2.setRelated(product3); 26 | product3.setRelated(product1); 27 | 28 | Product2a[] products = {product1, product2, product3}; 29 | 30 | writeGSON(products); 31 | } 32 | 33 | private static void write(Product2a[] pProducts) throws IOException 34 | { 35 | JSONArray products = new JSONArray(); 36 | for( Product2a product : pProducts ) 37 | { 38 | products.put(product.toJSONObject()); 39 | } 40 | try( PrintWriter out = new PrintWriter(new FileWriter("products2.json"))) 41 | { 42 | products.write(out); 43 | } 44 | } 45 | 46 | private static void writeGSON(Product2a[] pProducts) throws IOException 47 | { 48 | Gson gson = new Gson(); 49 | try( PrintWriter out = new PrintWriter(new FileWriter("products-g.json"))) 50 | { 51 | out.write(gson.toJson(pProducts)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/SerializationExampleXML1.java: -------------------------------------------------------------------------------- 1 | package lecture1; 2 | 3 | import java.beans.DefaultPersistenceDelegate; 4 | import java.beans.Encoder; 5 | import java.beans.Statement; 6 | import java.beans.XMLDecoder; 7 | import java.beans.XMLEncoder; 8 | import java.io.BufferedInputStream; 9 | import java.io.BufferedOutputStream; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.util.Iterator; 14 | 15 | public class SerializationExampleXML1 16 | { 17 | public static void main(String[] args) throws Exception 18 | { 19 | Product3 product1 = new Product3("TV", 1); 20 | Product3 product2 = new Product3("Phone", 2); 21 | Product3 product3 = new Product3("Radio", 3); 22 | 23 | product1.setRelated(product2); 24 | product2.setRelated(product1); 25 | 26 | product1.setProperty("screen", "bw"); 27 | product1.setProperty("weight", "950"); 28 | product2.setProperty("os", "Win"); 29 | product3.setProperty("default", "CBC"); 30 | 31 | Product3[] products = {product1, product2, product3}; 32 | write(products); 33 | 34 | Product3[] readProducts = read(); 35 | 36 | print(readProducts); 37 | } 38 | 39 | private static void write(Product3[] pProducts) throws IOException 40 | { 41 | try(XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("products.xml")))) 42 | { 43 | configureXMLEncoder(encoder); 44 | encoder.writeObject(pProducts); 45 | } 46 | } 47 | 48 | private static Product3[] read() throws IOException 49 | { 50 | try(XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream("products.xml")))) 51 | { 52 | return (Product3[])decoder.readObject(); 53 | } 54 | } 55 | 56 | private static void print(Product3[] pProducts) 57 | { 58 | for( Product3 product : pProducts ) 59 | { 60 | System.out.println(product); 61 | } 62 | } 63 | 64 | private static void configureXMLEncoder(XMLEncoder pEncoder) 65 | { 66 | pEncoder.setPersistenceDelegate(Product3.class, new DefaultPersistenceDelegate() 67 | { 68 | protected void initialize(Class pType, Object pOldInstance, Object pNewInstance, Encoder pOut) 69 | { 70 | super.initialize(pType, pOldInstance, pNewInstance, pOut); 71 | for(Iterator namesIt = ((Product3)pOldInstance).getPropertyNames(); namesIt.hasNext(); ) 72 | { 73 | String property = namesIt.next(); 74 | pOut.writeStatement( new Statement(pOldInstance, "setProperty", new Object[]{ property, ((Product3)pOldInstance).getProperty(property) }) ); 75 | } 76 | } 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /modules/artifacts/module-10/lecture1/pretty.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "id" : 1, 3 | "name" : "TV", 4 | "properties": { 5 | "screen":"bw", 6 | "weight":"950" 7 | } 8 | }, 9 | 10 | {"id":2,"name":"Phone","properties":{"os":"Win"}}, 11 | 12 | {"id":3,"name":"Radio","properties":{"default":"CBC"}} 13 | ] -------------------------------------------------------------------------------- /modules/figures/m01-DesignSpace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m01-DesignSpace.png -------------------------------------------------------------------------------- /modules/figures/m01-objectDiagram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m01-objectDiagram1.png -------------------------------------------------------------------------------- /modules/figures/m01-objectDiagram2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m01-objectDiagram2.png -------------------------------------------------------------------------------- /modules/figures/m01-scopes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m01-scopes.png -------------------------------------------------------------------------------- /modules/figures/m02-classDiagramExample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-classDiagramExample1.png -------------------------------------------------------------------------------- /modules/figures/m02-classDiagramNotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-classDiagramNotation.png -------------------------------------------------------------------------------- /modules/figures/m02-isp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-isp1.png -------------------------------------------------------------------------------- /modules/figures/m02-isp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-isp2.png -------------------------------------------------------------------------------- /modules/figures/m02-iterator1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-iterator1.png -------------------------------------------------------------------------------- /modules/figures/m02-iterator2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-iterator2.png -------------------------------------------------------------------------------- /modules/figures/m02-strategy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-strategy1.png -------------------------------------------------------------------------------- /modules/figures/m02-strategy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m02-strategy2.png -------------------------------------------------------------------------------- /modules/figures/m03-closure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m03-closure.png -------------------------------------------------------------------------------- /modules/figures/m03-debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m03-debugger.png -------------------------------------------------------------------------------- /modules/figures/m03-objectDiagram1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m03-objectDiagram1.png -------------------------------------------------------------------------------- /modules/figures/m03-stateDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m03-stateDiagram.png -------------------------------------------------------------------------------- /modules/figures/m03-stateDiagramEx1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m03-stateDiagramEx1.png -------------------------------------------------------------------------------- /modules/figures/m04-CFG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m04-CFG.png -------------------------------------------------------------------------------- /modules/figures/m04-Stubs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m04-Stubs.png -------------------------------------------------------------------------------- /modules/figures/m04-TestSuiteOrganization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m04-TestSuiteOrganization.png -------------------------------------------------------------------------------- /modules/figures/m05-aggregation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-aggregation.png -------------------------------------------------------------------------------- /modules/figures/m05-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-command.png -------------------------------------------------------------------------------- /modules/figures/m05-composite1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-composite1.png -------------------------------------------------------------------------------- /modules/figures/m05-composite2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-composite2.png -------------------------------------------------------------------------------- /modules/figures/m05-compositedecorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-compositedecorator.png -------------------------------------------------------------------------------- /modules/figures/m05-decorator-sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-decorator-sequence.png -------------------------------------------------------------------------------- /modules/figures/m05-decorator1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-decorator1.png -------------------------------------------------------------------------------- /modules/figures/m05-demeter1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-demeter1.png -------------------------------------------------------------------------------- /modules/figures/m05-demeter2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-demeter2.png -------------------------------------------------------------------------------- /modules/figures/m05-demeter3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-demeter3.png -------------------------------------------------------------------------------- /modules/figures/m05-exercise1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-exercise1.png -------------------------------------------------------------------------------- /modules/figures/m05-sequencedemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-sequencedemo.png -------------------------------------------------------------------------------- /modules/figures/m05-toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m05-toolbar.png -------------------------------------------------------------------------------- /modules/figures/m06-ComponentGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-ComponentGraph.png -------------------------------------------------------------------------------- /modules/figures/m06-JavaFX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-JavaFX.png -------------------------------------------------------------------------------- /modules/figures/m06-ObserverSummary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-ObserverSummary.png -------------------------------------------------------------------------------- /modules/figures/m06-basicObserver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-basicObserver.png -------------------------------------------------------------------------------- /modules/figures/m06-callbacks1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-callbacks1.png -------------------------------------------------------------------------------- /modules/figures/m06-completeObserver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-completeObserver.png -------------------------------------------------------------------------------- /modules/figures/m06-cs1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs1.png -------------------------------------------------------------------------------- /modules/figures/m06-cs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs2.png -------------------------------------------------------------------------------- /modules/figures/m06-cs3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs3.png -------------------------------------------------------------------------------- /modules/figures/m06-cs4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs4.png -------------------------------------------------------------------------------- /modules/figures/m06-cs5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs5.png -------------------------------------------------------------------------------- /modules/figures/m06-cs6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-cs6.png -------------------------------------------------------------------------------- /modules/figures/m06-dependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-dependencies.png -------------------------------------------------------------------------------- /modules/figures/m06-framework-a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-framework-a.png -------------------------------------------------------------------------------- /modules/figures/m06-framework-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-framework-b.png -------------------------------------------------------------------------------- /modules/figures/m06-luckyNumber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m06-luckyNumber.png -------------------------------------------------------------------------------- /modules/figures/m07-abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m07-abstract.png -------------------------------------------------------------------------------- /modules/figures/m07-fieldinit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m07-fieldinit.png -------------------------------------------------------------------------------- /modules/figures/m07-inheritance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m07-inheritance.png -------------------------------------------------------------------------------- /modules/figures/m07-polymorphism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m07-polymorphism.png -------------------------------------------------------------------------------- /modules/figures/m07-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m07-template.png -------------------------------------------------------------------------------- /modules/figures/m08-FileSystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m08-FileSystem.png -------------------------------------------------------------------------------- /modules/figures/m08-robot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prmr/SoftwareDesign/1011fc4a495f518e689a1f10ff950a332d7ff36c/modules/figures/m08-robot.png --------------------------------------------------------------------------------