Submission deadline: Monday, September 9 提交截止日期:9 月 9 日星期一
Discussion deadline: Friday, September 27 讨论截止日期:9 月 27 日星期五
General Instructions 一般说明
In object-oriented design, a pattern is a general repeatable solution to a commonly occurring problem. 在面向对象的设计中,模式是针对常见问题的一般可重复解决方案。 It is not a finished design that can be transformed directly into code, but rather a description or template for how to solve a problem that can be used in many different situations. 它不是一个可以直接转化为代码的设计成品,而是一个可以在许多不同情况下使用的关于如何解决问题的描述或模板。 Effective software design requires considering issues that may not become visible until later in the implementation. Design patterns can handle such issues and speed up development process by providing tested, proven development paradigms. 要进行有效的软件设计,就必须考虑到一些在实施后期才会显现的问题。设计模式可以处理这些问题,并通过提供经过测试和验证的开发范例来加快开发进程。
This practical task introduces you to the State Design Pattern and make you familiar with problems posed by event-driven systems. The state pattern is close to the concept of finite-state machines and allows an object to alter its behaviour as its internal state changes. 本实践任务将向你介绍状态设计模式,让你熟悉事件驱动系统带来的问题。状态模式接近于有限状态机的概念,允许对象在内部状态发生变化时改变其行为。 It can be interpreted as a strategy pattern, which is able to switch a strategy through invocations of methods defined in the pattern's interface. 它可以被理解为一种策略模式,通过调用模式接口中定义的方法来切换策略。
This pattern helps to achieve the following. 这种模式有助于实现以下目标。
It makes a class independent of how state-specific behaviour is implemented. 它使一个类独立于特定状态行为的实现方式。
It enables to add new states by defining new state classes. 它可以通过定义新的状态类来添加新的状态。
It allows to change the class's behaviour at run-time by changing its current state object. 它允许在运行时通过更改当前状态对象来改变类的行为。
Indeed, implementing state-specific behaviour directly within a class is inflexible because it commits the class to a particular behaviour and makes it impossible to add a new state or change the behaviour of an existing state later independently from (without changing) the class. 事实上,直接在一个类中实现特定状态的行为是不灵活的,因为这样做会使该类服从于特定的行为,以后就不可能独立于(不改变)该类而添加新的状态或改变现有状态的行为。 The state pattern overcomes this challenge. First, it defines separate (state) objects that encapsulate state-specific behaviour for each state. This implies an interface (state) for performing state-specific behaviour. 状态模式克服了这一难题。首先,它定义了独立的(状态)对象,为每个状态封装了特定于状态的行为。这就意味着要有一个接口(状态)来执行特定状态的行为。 It then defines classes that implement the interface for each state. Second, it ensures that a class delegates state-specific behaviour to its current state object instead of implementing state-specific behaviour directly. 然后,它定义了为每个状态实现接口的类。其次,它确保一个类将特定于状态的行为委托给其当前状态对象,而不是直接实现特定于状态的行为。
Now, let's focus on the task description. Imagine that you have been hired by an electronics company to build a software for a simple Reaction-Timer game. The Reaction-Timer machine has two inputs: 现在,让我们把重点放在任务描述上。想象一下,你受雇于一家电子公司,为一款简单的 "反应-计时器 "游戏开发一款软件。反应计时器有两个输入端:
a coin-slot that starts the game and 启动游戏的硬币槽和
a Go/Stop button that controls it. 一个用于控制的 "前进/停止 "按钮。
There is also a display that indicates to the player what he/she should do next. The machine must behave as follows. 此外,还有一个显示屏指示玩家下一步该做什么。机器必须按以下步骤操作。
Initially, the display shows 'Insert coin', and the machine waits for the player to do so. 开始时,显示屏会显示 "投币",机器等待玩家投币。
When the player inserts a coin, the machine displays 'Press GO!' and waits for the player to do so. 玩家投币后,机器会显示 "按 GO!"并等待玩家按下。
When the player presses the Go/Stop button, the machine displays 'Wait...' for a random time between 1.0 and 2.5 seconds. After the random delay expires, the machine displays a time-value that increments every 10 milliseconds, starting at zero. 当玩家按下 "开始/停止 "按钮时,机器会显示 "等待......",时间随机在 1.0 至 2.5 秒之间。随机延时结束后,机器会显示一个时间值,从 0 开始,每 10 毫秒递增一次。 The player must now press the Go/Stop button as soon as possible - the goal of the game is to show fast reactions! 玩家现在必须尽快按下 "开始/停止 "按钮--游戏的目的是展示快速反应能力! If the player presses the Go/Stop button during the random delay period, i.e. the player tries to "guess" when the delay will expire, the machine aborts the game and immediately demands another coin. 如果玩家在随机延迟期间按下 "开始/停止 "按钮,即玩家试图 "猜测 "延迟何时结束,机器就会中止游戏并立即要求玩家再投一枚硬币。
To put it simply, there is no reward for trying to cheat! If the user has not pressed stop after two seconds, the machine will stop automatically - no living person could be that slow! Whether the player has pressed the Go/Stop button within the two seconds waiting time period or not, the machine displays the final timer value for three seconds, then the game is over until another coin is inserted.
If the player presses the Go/Stop button while the measured reaction time is being displayed, the machine immediately displays 'Insert coin'.
Start with exploring the two provided templates for the program that you will need to write. You are free to select any of these two. The first option is a Windows Forms application, which is suitable for MS Visual Studio on Windows platform. The second option is a Console Application, which is cross-platform and appropriate for both MS Visual Studio and Visual Studio Code. Regardless the template you will select, your program must consist of the following parts. - A main program module, which is ready for you and provided in the SimpleReactionMachine.cs file. This module emulates the Reaction-Timer machine itself and serves as a base to test and use the controller that is a part of your particular task. The controller will drive the machine. - A GUI component for the Reaction-Timer machine, which is also ready and provided. Its configuration depends on the template you will select. In both cases, it includes a button labelled 'Coin inserted' and a button labelled 'Go/Stop'. It also has a display region to show the messages of the machine. The GUI conforms to the IGui interface. - Simple Reaction Controller is the module that you will need to implement. This is the main part of the exercise. You are free to write this component in any way you choose if it implements the required IController interface. Specifically, your task is to add a SimpleReactionController.cs source code file and complete a new
SimpleReactionController class that functions as prescribed for the Reaction-Timer machine above. The IController interface is included in the templates.
2. Spend some time to elaborate on how the Reaction-Timer machine as an eventdriven system should act. Explore the notes on the implementation of finite-state machines (FSM), of which the Reaction-Timer machine is an example. Here, we refer to the book chapter attached to the project. To proceed to the next step, you should first complete the design of the system by building what is called as a state-transition diagram. The attached document has a number of very similar examples and will guide you in decision making on the number of required states and their properties as well as possible transitions. This step is crucial as the translation from the diagram to a program code is quite straightforward, sure if your diagram is correct. Certainly, many programmers are tempted to say "I can get this right without a FSM". Unfortunately, this statement is rarely true. Most often, the program that results will be hard to understand, hard to modify, and will not be correct. The document will try to convince you of the advantage of FSMs. It explains how to convert the correct design into errorless code.
3. If you trust your design and it meets the machine's specification, it is time to think about the code you will need to write. Again, we refer to the attached document and examples that it describes. Some video materials referred at the bottom of this task sheet will also help you with the structure of the correct implementation. You should know what you are going to write before you start. Focus on the use of the given IController interface and exploit the dynamic-dispatch mechanism that eliminates the need for the switch statement. In addition, search and read about implementation of so-called nested (also called inner) classes in C# that allow to define a class within another class. This will allow you to strengthen encapsulation in your program and manipulate the states.
4. It is finally the time for coding. If you prefer to build a console application, import the files from the 'SimpleReactionMachine Console’ directory. If your choice is a Windows Forms application, then open the existing project from 'SimpleReactionMachine WinForms', which will ask you to add the missing SimpleReactionController.cs file to the prepared project. In both cases, you will face compilation errors due to the lack of the required source code file. Therefore, add the SimpleReactionController class and make sure that it implements the IController interface and contains the following methods.
void Connect( IGui gui, IRandom rng ); Connects the controller to the specified GUI component, which implements the IGui interface, and the specified random number generator.
void Init( ); Initialises the controller.
void CoinInserted( ); Reacts on the insertion of a coin into the machine.
void GoStopPressed( ); Reacts on the pressing of the Go/Stop button of the machine. - void Tick( ); Reacts on the Tick event addressed to the controller.
Obviously, your controller needs a source of timing information. You must obtain this information by implementing the Tick method in your controller. The main SimpleReactionMachine class guarantees to call this method every 10 milliseconds. Do not use any other timer. Furthermore, your controller also needs a source of random numbers. You must obtain your random numbers by calling the GetRandom() method of the provided generator. Do not use the Random class. Note that time must be displayed with two decimal places. For example, a value of 1.5 seconds must be shown as 1.50
5. As you progress with the implementation of the SimpleReactionController class, you should start using the Tester class from the 'SimpleReactionMachine Tester' directory of the project. It will allow you to thoroughly test the class you are developing aiming on the coverage of all potential logical issues and runtime errors. This (testing) part of the task is as important as writing the controller itself. To activate testing, make a separate Console Application project using the files in the 'SimpleReactionMachine Tester’ and import your current version of the SimpleReactionMachine class. Explore the Tester class to get details of the tests we prepared for you and the sequence that they are applied. If you fail a test, then the logic (behaviour) of your controller is incorrect. The following displays the expected printout produced by the attached Tester, specifically by its Main method.
test A: passed successfully
test B: passed successfully
test C: passed successfully
test D: passed successfully
test E: passed successfully
test F: passed successfully
test G: passed successfully
test H: passed successfully
test I: passed successfully
test J: passed successfully
test K: passed successfully
test L: passed successfully
test M: passed successfully
test N: passed successfully
test O: passed successfully
test P: passed successfully
test Q: passed successfully
test R: passed successfully
test S: passed successfully
test T: passed successfully
test U: passed successfully
test V : passed successfully
test W: passed successfully
test X: passed successfully
test Y: passed successfully
test Z: passed successfully
test a: passed successfully
test b: passed successfully
test c: passed successfully
test d: passed successfully
test e: passed successfully
test f: passed successfully
test g: passed successfully
test h: passed successfully
test i: passed successfully test
j: passed successfully
test k: passed successfully
test l: passed successfully
Summary: 38 tests passed out of 38
6. Finally, remember that this task is primarily about object-oriented design rather than pure coding. If you follow the instructions, your code should not be longer than (approximately) 200 lines. This is how long our solution is. Our past experience says that students coding without design will likely end up with several times longer code, which is usually messy and inefficient because of many plugs and conditional statements.
Further Notes - Explore the attached book chapter entitled "Notes on Finite State Machines" to learn about the concepts you will need to complete this project correctly. Especially, focus on the examples of the state-transition diagrams as they will help you sketch your FSM.
After showing the basic Reaction-Timer machine to potential customers, the marketing department has found that some of them would like to pay more for a deluxe ReactionTimer that allows multiple games after payment of a single coin. In this machine, when a coin is inserted, the player is allowed to play three games. The operating sequence is very similar to the previous one:
?insert coin,
3 press the Go/Stop button,
? 3 wait random delay,
? press the Go/Stop button, then
? display the time for three seconds.
If the machine has not yet completed three games, it then displays 'Wait...'. Subsequently, it waits a (new) random delay and proceeds further as in the first game. After displaying the time for the last game, the machine shows the average time 'Average ' (e.g. Average ) for five seconds, then the game is over.
The enhanced controller should fulfil the following requirements.
? If after inserting the coin the player fails to press Go/Stop within ten seconds, the game is over.
? If the player presses the Go/Stop button during the waiting period, the game is aborted, and the average value is not displayed. Once again, no reward for cheating!
? If the player presses the Go/Stop button while the machine is displaying a reactiontime value, the machine immediately moves on to the next game (or shows the average time) without waiting for the full three seconds of display time.
? If the player presses the Go/Stop button while the machine is displaying the average time, the game is
immediately over.
7. As in the original task, you are expected to start with the design of the event-driven system. Specifically, revise your current state-transition diagram and decide whether the existing states are sufficient to model the work of the enhanced system. Are there any new specific transitions that need to be handled by the controller? When you trust your design, implement it in code. You must produce a new EnhancedReactionController class written in the EnhancedReactionController.cs source code file.
8. Because this is a high distinction task, it does not provide you with a set of tests as it was done for thebasic Reaction-Timer machine. Actually, you are expected to propose and write your own test cases similar to what you have seen previously in Task 5.3. You should not just copy and paste the original tests.
Develop your own test instances that you feel important and well-motivated. Thus, we encourage you to thoroughly test your code against both expected and unexpected scenarios. Your work on testing will result in a separate project with Tester as a new main class to check the required EnhancedReactionController class. Please, make sure that you comment every single test that you add stating its purpose. You must have enough tests: The total number should not be less than what you found in q1-6 though you should also focus on the effectiveness of the tests, not just the quantity.
The following video materials will give you more insights on the finite state machines, their relevance to the state design pattern, and how to implement the pattern in code. . https://www.youtube.com/watch?v=N12L5D78MAA