Release Date: 3/18/2005
| Update Date: 3/18/2005
Pratap Lakshmanmicrosoft Corporation
Applicable to: Microsoft Visual J # .Netvisual Studio .NET
Summary: This article examines design solutions that can be used in Microsoft Visual J # .NET. This is the first part of the article series (including three parts) of how to solve the problem through J #.
This page
Introduction Problem Simply Solution Explains the class as the type of interface as a type factory policy design practice
In "Are Toy Problems Useful" [1], when Knuth summarizes the practicality of the toy problem, the educational value of a problem depends primarily on the application of the thinking process in the process of solving this problem. The frequency is high, and the usefulness of the answer to the question is nothing to do.
In this article, we will introduce how to solve problems through J #. Although these issues may be similar to the toy problem, we hope that you can find what the thinking process for solving these issues is helpful. These issues are simple enough and therefore do not cover the corresponding solution. The solution does not use any programming skills, and if you select the design obtained with different language, you can reuse these designs.
The other two parts in this article are the second part Delegates In Action With J # and the third part Solving Constraints with J #.
If you have specific questions or feedback, send us an email through jsfeedbk@microsoft.com. We look forward to your feedback.
Introduction
"Design" is driven by context. For products, this context refers to its use, its survival, materials, time factors, mode of use, and the like. The design of single aircraft is not suitable for aerospace planes. The design of the motor boat is not suitable for the aircraft carrier.
Software system design is not different. We must consider various problems, such as (only examples only):
•
performance
•
portability
•
What algorithms and data structures are used?
•
Control interaction between various parts
In view of this, good design usually has the following characteristics:
•
Description appropriate decomposition process
•
Modularity
•
Low coupling
•
flexible
•
Tape
We must consider the implementation when designing design.
We will use an example problem and try to explain the above problems.
This tutorial builds an example on a plurality of complete solutions, and improves it through the simple mode [2] in each step; in this process, we will explain the interface as a type " , "Factory", "strategy" and other modes.
Back to top
problem
Generating players may be used to play the six-sided dice of the game.
Back to top
Simple solution
Our purpose is not to win in any programming competition! All the work we need to do is to generate a simple six-sided dice.
We describe the dice as follows:
•
Dice is something that players can "throw". The value of the dice (on top) is the result of this "throw" operation.
That is, we describe the dice in full accordance with the operation we can perform ("throw").
We implemented this solution in App.jsl.
Listing 1.
// file: app.jsl
Public Class APP
{
Public static int roll ()
{
Java.util.random r = new java.util.random ();
Return (1 R.NextINT (6));
}
Public static void main (string [] args)
{
// roll multiple times
For (int i = 0; i <10; i )
{
System.out.println (roll ());
}
}
Back to top
Use the class as the type
Our last solution is not able to solve some problems:
•
We did not get the impression of using the dice.
•
What if we need to be ten dice? What code do we need to modify?
•
What if we need more than one dice? What code do we need to modify?
•
The code has a "magic" number (1, 6).
•
What is the relationship between roll () and dice? Is it captured in our program? Or is it existing in the programmer's mind?
•
What is the relationship between roll () and the number of faces on the dice?
We need to better express the dice.
We describe the dice as follows:
•
The dice is a thing.
•
Dice is something that players can "throw".
•
"Throw" operation can throw a dice.
•
The value of the dice (on top) is the result of this "throw" operation.
In addition, let us eliminate the limit of the number of faces that can have the dice. We will parameterize it.
This dice is implemented in Dice.jsl.
The dice is used in the application app.jsl.
Listing 2.
// file: dice.jsl
// this represents a die
Public Class Dice
{
Public Dice (INT i)
{
Numsides = i;
}
Public int SIDES ()
{
Return Numside;
}
// Number of Sides on the Die
Private int Numside;
}
// file: app.jsl
Public Class APP
{
Public Static Int Roll (Dice D)
{
Java.util.random r = new java.util.random ();
Return (1 R.NextINT (D.SIDES ()));
}
Public static void main (string [] args)
{
// Create a 6 Sided Die
Dice D1 = New Dice (6);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D1));
}
// Create An 8 Sided Die
Dice D2 = New Dice (8);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D2));
}
}
}
Back to top
Use the interface as a type
Recommoding module
Our previous solution allows us to have more than one dice.
Let us introduce the concept of client and server before continuing to discuss. Server provides a service. In this case, DICE.JSL provides a dice service. "Client" is app.jsl, which uses the service.
What is "coupling" between the client and the server? How close is the connection between the client and the dice used? What happens if we want to make some changes to the dice (for example, introducing some new data / methods)? Does the client need to recompile?
Is that reasonable? What if you use this dice in multiple files? What if it is used by multiple clients?
Use the interface as a type
We introduced a dice interface in rollable.jsl. The dice class in dice.js is implemented. Client app.jsl only works according to this interface.
Listing 3.
// file: rollable.jsl // an interface representing a die
Interface Rollable
{
Public int SIDES ();
}
// file: dice.jsl
// this represents the die; it now imports
// the Rollable Interface
Public Class Dice Implements ROLLABLE
{
Public Dice (INT i)
{
Numsides = i;
}
Public int SIDES ()
{
Return Numside;
}
// Number of Sides on the Die
Private int Numside;
}
// file: app.jsl
Public Class APP
{
Public Static Int Roll (Rollable D)
{
Java.util.random r = new java.util.random ();
Return (1 R.NextINT (D.SIDES ()));
}
Public static void main (string [] args)
{
// Create a 6 Sided Die
ROLLABLE D1 = New Dice (6);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D1));
}
// Create An 8 Sided Die
Rollable D2 = New Dice (8);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D2));
}
}
}
Have you seen other areas we can improve?
Back to top
factory
coupling
Our previous solution has caused problems associated with coupling between clients and servers.
Our design must support the independent evolution of the client and server.
Therefore, we need to reduce the coupling between them to the lowest level.
As is currently realized, the implementation of the dice is "visible" for the client. This means that when we change the dice class next time, the client needs to recompile. There is no significant separation between the client and the server.
Let us separate the client and the server significantly.
We created a new class - DiceFactory.jsl - manages the creation of the dice object. The client now only processes according to the plant. This also provides the server with the flexibility of managing the storage of dice. The client no longer needs to know the following information: Where is the scorpion? Is the dice allocated on a pile? Is the dice allocated from the pre-allocated "Dice" collection? and many more.
Now, the following files provide a dice service (assuming life is DICE.dll).
Dice.jsl
Rollable.jsl
DiceFactory.jsl
App.jsl is the client (reference DICE.dll).
The "actual" of the dice means that it is no longer (and no need) "visible" for the client. The coupling between the client and the server is limited to DiceFactory.jsl and interface rollable.jsl.
We have isolated the client with the server!
Listing 4.
// file: dice.jsl
// this represents a die
Public Class Dice Implements ROLLABLE
{
Public Dice (INT i)
{
Numsides = i;
}
Public int SIDES ()
{
Return Numside;
}
PRIVATE INT NUMSIDES;
// file: rollable.jsl
// the interface representing a die
Interface Rollable
{
Public int SIDES ();
}
// file: DiceFactory.jsl
// this class handles the creation of dice
Public Class DiceFactory
{
Public Static Rollable Create (INT i)
{
Rollable D = New Dice (i);
Return D;
}
}
// file: app.jsl
Public Class APP
{
Public Static Int Roll (Rollable D)
{
Java.util.random r = new java.util.random ();
Return (1 R.NextINT (D.SIDES ()));
}
Public static void main (string [] args)
{
// Creation of Dice is Done Through The Factory
// Create a 6 Sided Die
ROLLABLE D1 = DiceFactory.create (6);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D1));
}
// Create An 8 Sided Die
ROLLABLE D2 = DiceFactory.create (8);
For (int i = 0; i <10; i )
{
System.out.println (ROLL (D2));
}
}
}
Back to top
Strategy
flexibility
Keep in mind that we are using dice in the game program - gamble by people, or like in casinos.
No casino likes to lose. Therefore, one of the requirements is to make the throwing dice with predictable results. This is generally called "loading" dice. If the casino starts, they switched to the "loaded" dice. Then they can beat you and start making money.
Under normal circumstances, the dice has the loading of "randomness"; that is, the result will be the random number in the number ranges of the dice.
test
How do game developers test the game? If the result of the throwing is random, it is difficult to write test cases (although in this example, the results will be in the fixed range). We must ensure that the predictable results are obtained in order to effectively test our software.
Strategy
We must change the "loading" policy without re-generating our dice components. (We may even do not have corresponding source code!) Whether it is from flexibility or test perspectives, it is worthwhile to change the throw ("loading") dice at runtime.
solution
We abstract the dice in rollstrategy.jsl as an interface. Each dice is referenced by this interface to the instance of the RollStrategy object. We abstract "loading" an abstraction of a method on the dice interface, which allows us to set ROLLSTRATEGY to be used by the dice. This object indicates the policy to be used when the dice is used.
We enhance the dice in dice.jsl. We move the Roll method to App.jsl and create a randomroll class (randomroll.jsl) to indicate a random distribution policy. This is the default policy used by the dice.
We introduced a method for loading a dice in rollable.js, and implemented it in the dice dice dice.jsl. The ROLL function is now using this "loading" to perform a roll-in operation. We updated DiceFactory.jsl to set the default load of the dice when you created. We created a Cyclicroll policy in a similar manner in Cyclicroll.jsl.
Now, the client will initialize the desired roll strategy, set the policy on the dice, then throw the dice. Please note how we change it at runtime.
Server files (assuming life is DICE.dll).
Dice.jsl, randomroll.jsl,
DiceFactory.jsl, Rollable.jsl, Rollstrategy.jsl
Listing 5.
// file: dice.jsl
// this represents a die.
// Note That it can be 'loaded' with a rolling statate
Public Class Dice Implements ROLLABLE
{
Public int SIDES ()
{
Return Numside;
}
Public Dice (INT i)
{
Numsides = i;
LOAD = NULL;
}
Public Void Load (Rollstrategy R)
{
LOAD = R;
}
Public int roll ()
{
INT i = -1;
IF (Load! = NULL)
{
i = loading.roll ();
}
Return I;
}
Private int Numside;
PRIVATE ROLLSTRATEGY LOAD;
}
// file: randomroll.jsl
// this represents on Strategy of Loading a Die
Public Class Randomroll IMPLEments ROLLSTRATEGY
{
Public Randomroll (INT I, INT J)
{
From = i;
THROUGH = J;
}
Public int roll ()
{
Java.util.random r = new java.util.random ();
INT CEILING = THROUGH 1;
INT i = from r.nextint (ceiling);
Return I;
}
Private int from;
PRIVATE INT THROUGH;
}
// file: DiceFactory.jsl
// this class handles the creation of dice
// it loads the Die with a rolling strategy as
// Part of the initialization of a die
Public Class DiceFactory
{
Public Static Rollable Create (INT i)
{
Rollable D = New Dice (i);
Rollstrategy r = new randomroll (1, i);
D.LOAD (R);
Return D;
}
}
// file: rollable.jsl
// the interface representing a die
Interface Rollable
{
Public int SIDES ();
Public Void Load (Rollstrategy R);
Public int roll ();
}
// file: rollstrategy.jsl // the interface That Repesents a rolling strategy
Interface rollstrategy
{
Public int roll ();
}
Client files (reference DICE.dll).
App.jsl, cyclicroll.jsl
Listing 6.
// file: app.jsl
Public Class APP
{
Public Static Int Roll (Rollable D)
{
INT i = D. roll ();
Return I;
}
Public static void main (string [] args)
{
// Create a die; by DeaFult it is loadinged with
// the random rolling strategy
Rollable D = DiceFactory.create (6);
For (int i = 0; i <10; i )
{
System.out.println (roll (d));
}
// Explicitly Load It with a differentent
// rolling strategy at 'Run Time'
RollStrategy R = New Cyclicroll (1, D.SIDES ());
D.LOAD (R);
For (int i = 0; i <10; i )
{
System.out.println (roll (d));
}
}
}
// file: cyclicroll.jsl
// this represents One Strategy of Loading a Die
Public Class Cyclicroll IMPLEments ROLLSTRATEGY
{
Public Cyclicroll (Int i, int J)
{
From = i;
THROUGH = J;
Curval = from;
}
Public int roll ()
{
IF (Curval> Through)
{
Curval = from;
}
RETURN CURVAL ;
}
Private int curval;
Private int from;
PRIVATE INT THROUGH;
}
Only DiceFactory.jsl, Rollable.jsl, RollStrategy.jsl is "visible" for the client.
This implementation has the following characteristics:
•
Obviously separation between clients and servers
•
Modularity
•
Low counted (limited to interface level)
•
Support the independent evolution of clients and servers
•
We can change the strategy of the arrest at runtime (flexibility)
•
Support test
complete.
This completes our "Dice Design" project.
I hope you can learn some knowledge during this process!
Back to top
Design exercise
•
Unlike a single dice supporting multiple loading strategies, we can have a dice hierarchy, each of which supports different strategies. Please contact our best to discuss the advantages of this.
•
Unlike let the dice implement the Rollable interface, we can have an abstract class that represents the dice, and all the specific implementations of the dice are inherited from the abstraction class. Please contact our best to discuss the advantages of this.
•
Extend the dice to allow you to have different types of objects on each face. For example, the scorpion can have a letter, and it is also possible to have a picture.
reference
[1] Knuth, Donald, E., SELECTED PAPERS on Computer Science, Cambridge University Press, 1996 [2] Design Patterns: Elements of Reusable Object-Oriented Software "Gamma E. et al. Addison Wesley, 1995
Go to the original English page