catalog
For Experiment 3, the first step I think is to read the report. From the content and pages of the report, we can see that the task is very large and many, so we must carefully read the report. Read the report through and analyze it.
1, For ADT design
1.1 common design
At the beginning, according to the report, for the design of common features, to design an interface for PlanningEntry, six methods are written in the beginning: create a new plan item object, start, cancel, etc. Then in the specific implementation, if there is no more method, it will be added again. Then design a class CommonPlanningEntry to implement this interface. All use generics.
1.2 personalized design
The three application scenarios I chose are airplane, train, and curriculum
First, analyze their personalized characteristics, and then design ADT. Each color can be regarded as a feature, which integrates all the features of three application scenarios in the table. At the beginning, I divided location, resource, time and whether it can be blocked into four dimensions.
Position 3 dimension: a start point / end point that cannot be changed after design can be set in advance, a start point + a group of intermediate positions + an end point that cannot be changed after design can be set in advance, and a specific position that can be changed after design can be set in advance.
Resource 2 latitude: a single distinguishable resource, and multiple resources can be distinguished with order.
Time 2 dimension: you can set a start / end time pair of time, and a set of start / end time pairs of time.
Can block 2: Blocked, Unblocked
So at the beginning, all the features are combined to form a total of 1 + 3 + 2 + 2 + 2 = 10 classes.
Then design the class name and Rep for them.
For all their common operations, we have designed the PlanningEntry interface, so as long as the CommonPlanningEntry class implements all the methods of this interface.
For a Rep with a start and end Location, there should be two Location object stores: beginLocation and endLocation. For a starting point + a group of intermediate positions + an end point, what I originally designed is that all positions are stored in a list, which realizes both a group of intermediate positions and a starting and ending positions. Because the list is ordered, the first and last elements are the starting and ending positions. For specific Location, design a Location object to store it.
A single distinguishable resource can be stored with a generic R. Multiple distinguishable resources with order are implemented by list, which not only implements order, but also implements multiple resources.
According to the report (as shown below), we need to design a Timeslot class to represent a time pair. A start stop time pair is represented by a Timeslot object. For a set of start and end time pairs, we also use a list to represent and implement a set. Because the number order and location must correspond one by one, the subscript of each location corresponds to the corresponding time. Since there is only one time between the beginning and the end, I use null to represent the time I don't have.
For two Blocked, because their basic operations are the same, which is generally to get the status and modify the status, it is better to store the interface directly when storing later. So I designed an interface for EntryState. Both Unblocked and Blocked classes implement this interface. And the state mode should be implemented. I will introduce the state mode later.
But in the later specific implementation process, I found that the location and time are bound together, so it would be more convenient to combine the two features of location and time. So I reduced the two classes and merged the corresponding time dimension and location dimension together. Add a time pair Timeslot to a start and end point class to represent the start and end time. For a class in a specific location, a Timeslot is also added to represent the start and end time. For a class with a start and end location and a group of intermediate locations, add a Timeslot to represent the start and end location time. For the intermediate location, represent the Rep as a map < location, Timeslot >, and a location corresponds to a time pair.
In conclusion, a total of 1 + 3 + 2 + 2 = 8 classes are designed.
1.3 code display
public class BlockedStateEntryImpl implements BlockedStateEntry
public class ChangeableSpecificLocationEntryImpl implements ChangeableSpecificLocationEntry {// Specific location that can be changed after setting private Location specificLocation;// Store specific location private final Timeslot timeslot;// Starting and ending time is right
public class CommonPlanningEntryImpl<R> implements PlanningEntry<R> { private EntryState state;//Plan item status private final String name;//Plan item name
public class MultipleDustinguishResourseEntryImpl<R> implements MultipleDustinguishResourceEntry<R> { private List<R> list;
public class NotChangeableHaveMiddleLocationEntryImpl implements NotChangeableHaveMiddleLocationEntry { private final Location beginLocation;// Starting position private final Location endLocation;// End position private final Timeslot beginAndEndtimeslot;// Start and end position time pairs private final Map<Location, Timeslot> map;// Where and when to store the intermediate location
public class NotChangeableOneBeginAndEndLocationEntryImpl implements NotChangeableOneBeginAndEndLocationEntry { private final Location beginLocation;// Starting position private final Location endLocation;// End position private final Timeslot timeslot;// Time is right
public class SingleDistinguishleResourceEntryImpl<R> implements SingleDistinguishleResourceEntry<R> { private R resourse;
public class UnblockedStateEntryImpl implements UnblockedStateEntry
Finally, the two underlying classes of Location and Timeslot should be written completely.
public class Location { private final String name;// name private final Boolean shared;// Shareable or not public Location(String name, Boolean shared) { this.name = name; this.shared = shared; } public String getName() { return name; } public Boolean getShared() { return shared; } @Override public Location clone() { return new Location(name, shared); } @Override public int hashCode() {...} @Override public boolean equals(Object obj) {...}
public class Timeslot implements Cloneable { // Rep invariant: // startData start date // endData end date // sdf normalized to minutes // sdf1 regularization hours and minutes // sdf2 regularization date // Abstraction function: // AF(startTime,endTime) = // Generate a time pair with start date as startTime and end date as endTime // Safety from rep exposure: // Except startData, all data in endData is private and final, so it is immutable; // All get methods use defensive copy, so the external world cannot modify the variable startData and endData private Date startDate;// Start date private Date endDate;// End date private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");// Regularize to minutes private final SimpleDateFormat sdf1 = new SimpleDateFormat("HH:mm");// Regular hours and minutes private final SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");// Date of regularization public Timeslot(String startTime, String endTime) { try { this.startDate = sdf.parse(startTime); this.endDate = sdf.parse(endTime); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } checkRep(); } public Date getStartDate() {...} public Date getEndDate() {...} private void checkRep() {...} @Override public String toString() {...} @Override public Timeslot clone() {...} @Override public int hashCode() {...} @Override public boolean equals(Object obj) {...}
2, Design of scheme and design model
2.1 scheme design
2.1.1 decorator mode (scheme 6)First of all, the experimental manual gives a total of six schemes, because we have been talking about the main core of soft construction course is the delegation and decorator mode. So in the beginning, I chose between plan 5 and plan 6.
At first I tried the decorator mode. Follow what the teacher said in class.
If we construct the UML diagram of decorator pattern for Experiment 3, it is as follows.
code:
public interface PlanningEntry { /** * Start this project */ public void Begin(); /** * Cancel this plan item */ public void Cancel(); /** * Terminate this program item */ public void Compelete(); /** * * @return Return the name of this plan item */ public String GetPlanningEntryName();
public class CommmonPlanningEntry implements PlanningEntry { @Override public void Begin() {// Start, initialize resources // TODO Auto-generated method stub System.out.println("begin"); } @Override public void Cancel() { // TODO Auto-generated method stub } @Override public void Compelete() { // TODO Auto-generated method stub } @Override public String GetPlanningEntryName() { // TODO Auto-generated method stub return new String(); } }
public abstract class Decorator implements PlanningEntry { private final PlanningEntry planningEntry; public Decorator(PlanningEntry planningEntry) { // TODO Auto-generated constructor stub this.planningEntry = planningEntry; } public void Begin() { planningEntry.Begin(); } public void Cancel() { planningEntry.Cancel(); } public void Compelete() { planningEntry.Compelete(); } public String GetPlanningEntryName() { return planningEntry.GetPlanningEntryName(); } }
public class FlightEntry extends Decorator { public FlightEntry(PlanningEntry planningEntry) { super(planningEntry); } public void getBeginLocation() { System.out.println(1); } }
public class TrainEntry extends Decorator { public TrainEntry(PlanningEntry planningEntry) { super(planningEntry); } public void getMiddleLocations() { System.out.println(2); } }
public class Main { public static void main(String[] args) { FlightEntry flightEntry = new FlightEntry(new CommmonPlanningEntry()); TrainEntry trainEntry = new TrainEntry(new CommmonPlanningEntry()); flightEntry.Begin(); flightEntry.getBeginLocation(); trainEntry.Begin(); trainEntry.getMiddleLocations(); } }
Operation results
For the decorator pattern designed in the third experiment, it can't be realized like PPT. Only one layer of dolls can be realized, that is to say, three applications are regarded as three kinds of decorations. In specific applications, a common planning entry object can be decorated layer by layer. Decorate one floor at most.
According to the report, the delegation mode is well designed. For three specific applications, CommonPlanningEntry is inherited as the parent class. Then we implement each specific method. And adopt the delegation mode. Here is an example of flight and train.
public interface PlanningEntry<R> { /** * Start this project */ public void Begin(); /** * Cancel this plan item */ public void Cancel(); /** * Terminate this program item */ public void Compelete(); /** * * @return Return the name of this plan item */ public String GetPlanningEntryName(); /** * * @return Returns the status of the current plan item */ }
public class CommmonPlanningEntry<R> implements PlanningEntry<R> { private EntryState state;// Plan item status private final String name;// Plan item name public CommmonPlanningEntry(String name, EntryState state) { super(); this.name = name; this.state = state; } @Override public void Begin() {// Start, initialize resources // TODO Auto-generated method stub System.out.println("begin"); } @Override public void Cancel() { // TODO Auto-generated method stub } @Override public void Compelete() { // TODO Auto-generated method stub } @Override public String GetPlanningEntryName() { // TODO Auto-generated method stub return new String(); } }
public interface EntryInterface { // Location and time interface public interface ChangeableSpecificLocationEntry {// Interface of specific location that can be changed after setting /** * Change this specific position * * @param location Name you want to change */ public void ChangeLocation(String location);// Change location information /** * Get this address * * @return Other.Location Address of type */ public Location getLocation(); /** * * @return Return time to Timeslot */ public Timeslot getTimeslot(); } public interface NotChangeableHaveMiddleLocationEntry {// Interface with middle position that cannot be changed after setting /** * * @return Return the map of the time and place where the intermediate location is stored */ public Map<Location, Timeslot> getLocationAndTime(); /** * * @return Return to start position */ public Location getBeginLocation(); /** * Return to end position * * @return */ public Location getEndLocation(); /** * Determine whether the location is in the middle * * @param nowLocation * @return Is the middle position returns true */ public Boolean isMiddleLocation(Location nowLocation); /** * Time to get start and end positions * * @return */ public Timeslot getBeginAndEndTimeslot(); /** * location Whether the location time is within one hour before and after the data * * @param date current time * @param nowLocation position * @return If true is returned within one hour */ public Boolean onehourDatas(Date date, Location nowLocation); } public interface NotChangeableOneBeginAndEndLocationEntry {// Interface of a start and end position that cannot be changed after setting /** * * @return Return to start position */ public Location getBeginLocation(); /** * * @return Return to end position */ public Location getEndLocation(); /** * * @return Return time pairs of start and end positions */ public Timeslot getTimeslot(); /** * Determine whether the start time is one hour before and after the data * * @param date * @return Yes returns true */ public Boolean beforeBeginDatas(Date date); /** * Determine whether the end time is one hour before and after the data * * @param date * @return Yes returns true */ public Boolean afterEndDatas(Date date); } // Resource interface public interface SingleDistinguishleResourceEntry<R> {// Interface of a single distinguishable resource /** * * @return Return this resource */ public R getResource(); /** * Separate resources * * @param r resources */ public void AssignResource(R r); } public interface MultipleDustinguishResourceEntry<R> {// Multiple interfaces with sequential distinguishable resources /** * * @return Returns a list of all resources in order */ public List<R> getResources(); /** * Check if all resources in a plan item conflict * * @param r Resources to check * @return Conflict returns false, do not add; no conflict, returns true, add to resource */ public Boolean addResource(R r); /** * Remove a resource * * @param r Resources you want to remove * @return Remove successfully return true */ public Boolean removeResource(R r); } // blocked interface public interface BlockedStateEntry extends EntryState {// State blockable interface /** * Block the plan item */ public void Blocked(); } public interface UnblockedStateEntry extends EntryState {// Interface with non blocked state } public interface FlightPlanningEntry<R> extends NotChangeableOneBeginAndEndLocationEntry, UnblockedStateEntry, SingleDistinguishleResourceEntry<R> {// Interface of aircraft entry } public interface TrainPlanningEntry<R> extends NotChangeableHaveMiddleLocationEntry, MultipleDustinguishResourceEntry<R>, BlockedStateEntry {// Interface of train Entry } public interface CoursePlanningEntry<R> extends ChangeableSpecificLocationEntry, UnblockedStateEntry, SingleDistinguishleResourceEntry<R> {// Interface of course Entry } }
public interface EntryState { enum state { WAITING, ALLOCATED, RUNNING, BLOCKED, ENDED, CANCELLED }// Unallocated resource, allocated, started, pending, completed, cancelled public state getState(); public Boolean setState(state myState); }
public class FlightEntry<R> extends CommmonPlanningEntry<R> implements FlightPlanningEntry<R>{ private final NotChangeableOneBeginAndEndLocationEntry ncobaele; private SingleDistinguishleResourceEntry sdre = null; public FlightEntry(NotChangeableOneBeginAndEndLocationEntry ncobaele, EntryState use, String name) { super(name, use); this.ncobaele = ncobaele; } public Location getBeginLocation() { return ncobaele.getBeginLocation(); } ....//All methods not implemented by other interfaces }
public class TrainEntry<R> extends CommmonPlanningEntry<R> implements TrainPlanningEntry<R> { private final NotChangeableHaveMiddleLocationEntry nchmle; private MultipleDustinguishResourceEntry<R> mdre = null; public TrainEntry(NotChangeableHaveMiddleLocationEntry nchmle, EntryState bse, String name) { super(name, bse); this.nchmle = nchmle; } public Map<Location, Timeslot> getLocationAndTime() { return nchmle.getLocationAndTime(); } ....//All methods not implemented by other interfaces }2.2.3 comprehensive consideration and selection scheme
After consideration, scheme 5 was finally selected, because for Experiment 3, comparing 2.2.1 and 2.2.2, we can see that there is no difference between decorator mode and delegation mode. This decorator mode doesn't show the real advantages of decorators, just the common planning entry mode of every mechanical decoration,
So I only use the idea of delegation, so I finally chose scheme 5. It's unnecessary to decorate the common planning entry every time I apply for the decorator. If you encounter multiple (layer upon layer) projects later, you will take the decorator mode first. This time, we only adopted the delegation mode, scheme 5.
2.3 factory, iterator, facade, state, strategy design pattern
2.3.1factory
In factory mode, because parameters need to be passed, each plan item needs different parameters during construction, so there is no unified interface set.
public class CourseFactory { public static <R> CourseEntry<Teacher> emptyEntry(String name, String classroomString, String startTime, String endTime) { // TODO Auto-generated method stub Location specificLocation = new Location(classroomString, false); Timeslot timeslot = new Timeslot(startTime, endTime); ChangeableSpecificLocationEntry csle = new ChangeableSpecificLocationEntryImpl(specificLocation, timeslot); EntryState use = new UnblockedStateEntryImpl(); return new CourseEntry<Teacher>(csle, use, name); } }
public static <R> FlightEntry<R> emptyEntry(String beginLocationString, String endLocationString, String name, String startTime, String endTime) { // TODO Auto-generated method stub Timeslot timeslot = new Timeslot(startTime, endTime); Location beginLocation = new Location(beginLocationString, true); Location endLocation = new Location(endLocationString, true); NotChangeableOneBeginAndEndLocationEntry ncobaele = new NotChangeableOneBeginAndEndLocationEntryImpl( beginLocation, endLocation, timeslot); EntryState use = new UnblockedStateEntryImpl(); return new FlightEntry<R>(ncobaele, use, name); } }
public static <R> TrainEntry<TrainCarriage> emptyEntry(String name, List<String> locationList, List<String> beginTimeList, List<String> endTimeList) { // TODO Auto-generated method stub Map<Location, Timeslot> map = new HashMap<Location, Timeslot>(); if (locationList.size() == (beginTimeList.size() + 1) && beginTimeList.size() == endTimeList.size()) { Timeslot timeslot = new Timeslot(beginTimeList.get(0), endTimeList.get(endTimeList.size() - 1)); for (int i = 1; i < locationList.size() - 1; i++) { map.put(new Location(locationList.get(i), true), new Timeslot(beginTimeList.get(i), endTimeList.get(i - 1))); } NotChangeableHaveMiddleLocationEntry nchmle = new NotChangeableHaveMiddleLocationEntryImpl( new Location(locationList.get(0), true), new Location(locationList.get(locationList.size() - 1), true), timeslot, map); BlockedStateEntry bse = new BlockedStateEntryImpl(); return new TrainEntry<TrainCarriage>(nchmle, bse, name); } return null; } }2.3.2Iterator
There are five iterators in total. One is the course, which is sorted according to the time of class. For two aircraft, one is in order of departure time and the other is in order of arrival time. For two trains, one is based on the time of arrival location, and the other is based on the time of starting station.
Here is only one example of how to implement the Iterator sorted by time.
public class CourseStartTimeIterator<T> implements Iterator<CourseEntry<T>> { private final Map<CourseEntry<T>, Date> map = new HashMap<CourseEntry<T>, Date>();//Match each plan item to its time. Because the Data has the same situation, it cannot be set as key and set as value in the map. private final List<Date> startDates = new LinkedList<Date>();//In order to sort, sort the time from small to large private final Map<CourseEntry<T>, Boolean> flag = new HashMap<CourseEntry<T>, Boolean>();//When a plan item is fetched from the Map, if there is a plan item at the same time, it is used to record whether the plan item has been fetched. private int count = 0; public CourseStartTimeIterator(List<CourseEntry<T>> list) {//When the Iterator is initialized, a set of plan items is passed in, and each plan item corresponds to its time super(); for (int i = 0; i < list.size(); i++) { map.put(list.get(i), list.get(i).getTimeslot().getStartDate()); startDates.add(list.get(i).getTimeslot().getStartDate()); flag.put(list.get(i), false);//Set false for all fetches } Collections.sort(startDates);//Sort start time } private CourseEntry<T> getKey(Date date) {//Take out the plan item corresponding to a certain time for (CourseEntry<T> xCourseEntry : map.keySet()) { if (map.get(xCourseEntry).equals(date) && flag.get(xCourseEntry) == false) { flag.put(xCourseEntry, true);//After fetching the plan item, set the flag to true. return xCourseEntry; } } return null; } @Override public boolean hasNext() { // TODO Auto-generated method stub return count < startDates.size();//When the count counter is less than the number of all plan items, it is proved that there are still. } @Override public CourseEntry<T> next() { // TODO Auto-generated method stub Date xDate = startDates.get(count);//Because startdate has been sorted in chronological order, the time retrieved according to the index is from small to large. count++;//Counter plus one return getKey(xDate);//Then use the getKey method to get the plan item of the corresponding time. } }2.3.3facade
The facade pattern is to package all the methods used into a class. When calling, only the facade class is visible to the client, and the specific implementation of the internal is unknown.
Package all the methods used in the three applications into the facade class
All the methods used are written to the facade class.
Client call code
public static void main(String[] args) { facadePlanningEntry.begin(type, name); facadePlanningEntry.BoardSetInputTime(type, location, time); }2.3.4state
There is a slight difference between my state setting mode and what the teacher said. Because there are many states, the graph structure is used to write between them. However, the core idea is to delegate. When the state needs to be changed, delegate to the state class to complete it (call the setState method).
It can be divided into two categories. The state can be blocked, and the state can not be blocked. Both inherit the interface of EntryState and use the state mode. Change state delegate to yourself to complete.
In commonPlanningEntry, the state variable of type EntryState is stored, which is used to delegate itself to change state. When you need to change the state, call the setState method of state to modify your state. The setState method, based on the relationship between the two states, uses the structure of the graph to determine whether it can be converted.
For example, begin, cancel and other methods will call state's own setState method to modify the state when the state needs to be changed.
First, the state blockable interface
Here we use lab2's diagram to judge whether the two states can be converted. Transform the relationship into a graph in the constructor.
public interface BlockedStateEntry extends EntryState
Specific implementation class, public class BlockedStateEntryImpl implements BlockedStateEntry
public class BlockedStateEntryImpl implements BlockedStateEntry { Graph<EntryState.state> graph = Graph.empty(); private state Nowstate = state.WAITING; public BlockedStateEntryImpl() { super(); graph.set(state.WAITING, state.ALLOCATED, 1); graph.set(state.WAITING, state.CANCELLED, 1); graph.set(state.ALLOCATED, state.RUNNING, 1); graph.set(state.RUNNING, state.ENDED, 1); graph.set(state.ALLOCATED, state.CANCELLED, 1); graph.set(state.BLOCKED, state.RUNNING, 1); graph.set(state.BLOCKED, state.CANCELLED, 1); graph.set(state.RUNNING, state.BLOCKED, 1); } @Override public state getState() { // TODO Auto-generated method stub return Nowstate; } @Override public Boolean setState(state myState) { // TODO Auto-generated method stub if (graph.targets(Nowstate).containsKey(myState)) {//If it does not conform to the relationship in the diagram, it will not be set successfully and false will be returned. Nowstate = myState; return true; } else { return false; } } }
Second, the interface whose state cannot be blocked
public interface UnblockedStateEntry extends EntryState
Specific implementation class public class UnblockedStateEntryImpl implements UnblockedStateEntry
It also uses the storage structure of graph to judge whether the state can be converted.
public class UnblockedStateEntryImpl implements UnblockedStateEntry { Graph<EntryState.state> graph = Graph.empty(); private state Nowstate = state.WAITING; public UnblockedStateEntryImpl() { super(); graph.set(state.WAITING, state.ALLOCATED, 1); graph.set(state.WAITING, state.CANCELLED, 1); graph.set(state.ALLOCATED, state.RUNNING, 1); graph.set(state.RUNNING, state.ENDED, 1); graph.set(state.ALLOCATED, state.CANCELLED, 1); } @Override public state getState() { // TODO Auto-generated method stub return Nowstate; } @Override public Boolean setState(state myState) { // TODO Auto-generated method stub if (graph.targets(Nowstate).containsKey(myState)) {//If it does not conform to the relationship in the diagram, it will not be set successfully and false will be returned. Nowstate = myState; return true; } else { return false; } } }2.3.5strategy
I use the strategy pattern for the method of finding the preamble. Both implementation classes inherit the strategy interface
public interface strategy<R> { public PlanningEntry<R> findPreEntryPerResource(R r, PlanningEntry<R> e, List<? extends PlanningEntry<R>> entries); }
Strategy one calls the method of looking for the preamble of the external API class.
Strategy 2 realizes its own implementation method. There are two strategies.
/** * Extract the pre planning items for specific resources: * * @param name Name of plan item * @return Returns the string composed of the name, start position, end position, start and end time and status of the preceding plan item of this plan item; if not found, returns null */ public String findPreEntryPreResource(String name, strategy<Plane> strategy) {//The strategy to be implemented is added to the parameter. When different strategies are to be implemented, the class that inherits the strategy to be implemented is passed in between. if (!list.isEmpty()) { String zString = ""; FlightEntry<Plane> e = null; Plane r = null; Iterator<FlightEntry<Plane>> iterator = startTimeIterator(); while (iterator.hasNext()) { FlightEntry<R.Plane> flightEntry = (FlightEntry<R.Plane>) iterator.next(); if (flightEntry.GetPlanningEntryName().equals(name)) { e = flightEntry; r = flightEntry.getResource(); break; } } e = (FlightEntry<Plane>) strategy.findPreEntryPerResource(r, e, list);//The policy pattern is used here. if (e != null) { return zString + e.GetPlanningEntryName() + " " + e.getBeginLocation().getName() + "-" + e.getEndLocation().getName() + " " + e.getTimeslot().toString() + " " + e.getState().toString(); } else { return zString; } } return name; }
summary
To sum up, Experiment 3, after tens of thousands of lines of code and the application of various strategies, better connected all the learned knowledge. The method of designing ADT is deeply realized. When we get a requirement analysis method, we can make full use of various modes to realize our own programming skills. I think after the training of Experiment 3, we really realized the "disgust" of writing a software. And procedural apes.