Battleship - Controller |
![]() |
![]() |
Sonntag, den 10. Mai 2009 um 00:00 Uhr | |||||||||||
Seite 9 von 9
ControllerDie View und der Controller implementieren das klassische Strategy-Muster: Die View ist ein Objekt, das mit einer Strategie konfiguriert ist; der Controller liefert diese Strategie. Die View ist nur für die visuellen Aspekte der Anwendung zuständig; alle Entscheidungen über das Verhalten der Schnittstelle delegiert es an den Controller. Durch die Verwendung des Strategy-Musters bleibt die View außerhalb entkoppelt vom Model, denn es ist der Controller, der bei der Bearbeitung von Benutzereingaben für die Interaktion mit dem Model zuständig ist. Die View weiß nichts darüber, wie das vor sich geht. In dem Spiel Battleship gibt es nur einen Controller. Die Klasse BattleshipController erweitert die abstrakte Klasse AbstractController und stellt alle Methoden über das Interface ControllerInterface bereit. Neben den Methoden localGame und connect, finden sich im Controller viele weitere Methoden zur Steuerung des Spiels. Eine bekannte Methode, die Sie bereits in den Views sehen konnten ist die Methode shoot. Diese Methode führt bei einem Schuss alle wesentlichen Flußsteuerungen aus und ruft anschließend die entsprechende View wieder auf. /** * Shoots on a fields on the enemy board. * * @param player * @param c */ @Override public void shoot(PlayerInterface player, Coordinate c) { try { // Call model if( getModel().shoot( player.getId(), c ) ) { // If successful, check if it was the last ship and if // the game is over and inform the player player.showMessage( getModel().getString( "BATTLESHIP.STRIKE" ) ); if( getModel().isGameOver( player.getId() ) ) { // Proceed with the victory steps processVictory( player ); } else { // Game not over, move on player.move(); } } else { // If not successful, toggle player and let him move getActivePlayer().move(); } } catch(FieldOperationException e) { // Coords are not a valid field player.showMessage( e.getMessage() ); // Try again player.move(); } catch(InvalidShotException e) { // Shot wasn't valid player.showMessage( e.getMessage() ); // Try again player.move(); } } Der Controller generiert auch die Hauptoberfläche und hält einen Verweis auf ihre Instanz. Eine weitere Methode im Controller ist die Methode checkForUpdates. Diese Methode ruft eine Instanz der Klasse VersionManager auf. /** * Checks if updates are available. * * @param view the view called */ @Override public void checkForUpdates(ViewInterface view) { VersionManager manager = new VersionManager( getModel(), view ); manager.checkUpdateCheck( 3 ); // Perform update } Der VersionManager stellt eine Onlineverbindung mithilfe der Java-Klasse URLConnection her und überprüft ob es eine aktuellere Version von Battleship gibt, als die vom Benutzer gerade verwendete. Dazu wird eine Seite von Codeplanet kontaktiert, die mithilfe eines PHP-Skriptes die aktuelle Version übermittelt. Die Seite kann testweise über http://www.codeplanet.eu/files/battleship/version.php?version=1.0.0&language=de_DE aufgerufen werden. Findet das Programm eine aktuellere Version, wird der Nutzer über den installierten Systembrowser automatisch zum Installer der neuen Version weitergeleitet. Das Datum der letzten Aktualisierung wird in der Konfigurationsdatei gespeichert. Das Programm gestattet Aktualisierungen nur in bestimmten Zeitintervallen um ein Flooding der angegebenen Seite zu verhindern. /** * Checks for a new battleship version. This method will open * an URLConnection to a remote server to check for program updates. * If any updates are available, it will redirect the user browser * to the installer package. * * @param notifyNoUpdate if true, the user gets detailled informations * @return true, if successful */ public boolean checkForNewVersion(boolean notifyNoUpdate) { URL url = null; try { String base = "http://www.codeplanet.eu/files/battleship/version.php"; String version = "?version=" + Version.getShortVersion(); String language = "&language=" + model_.getConfig().getPreferences().getLocale(); String address = base + version + language; url = new URL( address ); } catch( MalformedURLException e ) { logger_.warning( model_.getString( "BATTLESHIP.UNABLE_TO_CHECK_FOR_UPDATED_VERSION" ) + "\n" + e.getMessage() ); if( notifyNoUpdate ) { view_.showMessageDialog( null, e.getMessage(), model_.getString( "BATTLESHIP.UNABLE_TO_CHECK_FOR_UPDATED_VERSION" ), 0, null ); } return false; } InputStream inputStream = null; InputStreamReader inputStreamReader = null; BufferedReader reader = null; // Try to get connection try { URLConnection urlConnection = url.openConnection(); urlConnection.setConnectTimeout( 5000 ); if( urlConnection instanceof HttpURLConnection ) { logger_.info( model_.getString( "BATTLESHIP.UPDATE_CHECK_WITH_HTTP" ) ); HttpURLConnection conn = (HttpURLConnection)urlConnection; if( conn.usingProxy() ) { logger_.info( model_.getString( "BATTLESHIP.HTTP_USING_PROXY" ) ); } } inputStream = urlConnection.getInputStream(); inputStreamReader = new InputStreamReader( inputStream ); reader = new BufferedReader( inputStreamReader ); String newVersLine = reader.readLine(); String curVersLine = Version.getShortVersion(); boolean newVersionAvailable = false; if( newVersLine != null && curVersLine != null ) { String newVersion = newVersLine; if( newVersion.compareTo(curVersLine) > 0 ) { newVersionAvailable = true; String[] filler = { newVersion }; if ( view_.showConfirmDialog( null, model_.getString( "BATTLESHIP.NEW_VERSION_AVAILABLE_DO_DOWNLOAD", filler ), model_.getString( "BATTLESHIP.NEW_VERSION_AVAILABLE", filler ), JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null) == JOptionPane.YES_OPTION ) { // Try to download installer try { BrowserManager.openURL( INSTALL_URL ); } catch( IOException e ) { view_.showMessageDialog( null, e.getMessage(), model_.getString( "BATTLESHIP.UNABLE_TO_OPEN_BROWSER" ), 0, null ); } } } } if( !newVersionAvailable && notifyNoUpdate ) { String[] filler = { Version.getShortVersion() }; view_.showMessageDialog( null, model_.getString( "BATTLESHIP.LATEST_VERSION_INSTALLED", filler ), model_.getString( "BATTLESHIP.NO_UPDATE_AVAILABLE" ), JOptionPane.INFORMATION_MESSAGE, null ); } } catch( IOException e ) { logger_.warning( model_.getString( "BATTLESHIP.UNABLE_TO_CHECK_FOR_UPDATED_VERSION" ) + " " + e.getMessage() ); if( notifyNoUpdate ) { view_.showMessageDialog( null, e.getMessage(), model_.getString( "BATTLESHIP.UNABLE_TO_CHECK_FOR_UPDATED_VERSION" ), JOptionPane.INFORMATION_MESSAGE, null ); } return false; } finally { if( inputStream != null ) { try { inputStream.close(); } catch( IOException e ) { } } if( inputStreamReader != null ) { try { inputStreamReader.close(); } catch( IOException e ) { } } if( reader != null ) { try { reader.close(); } catch( IOException e ) { } } } return true; } Das SpielZum Abschluß wollen wir einen Blick auf die Konstruktion des Spiels werfen. In der Klasse Main wird über Reflection die Basisklasse Game aufgerufen. public class Main { /** * For now, fire off the Main class. * In the future, this class may do some logic to find out available * startup classes and pick one (like the uis one does). * * @param args arguments */ public static void main(String[] args) throws IOException { try { Class startupClass = null; startupClass = Class.forName("eu.codeplanet.battleship.core.Game"); final Constructor constructor = startupClass.getConstructor(new Class[] { String[].class }); constructor.newInstance(new Object[] { args }); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } Die Instanzierung beginnt mit der Konstruktion des Models. Unmittelbar nach der Konstruktion wird die essentielle Methode initialize aufgerufen, die das Model initialisiert. Schlägt die Initialisierung aus irgendeinem Grund fehl, wird sofort abgebrochen. Nachdem das Model erfolgreich initialisiert wurde, wird der Controller instanziert. Diesem werden die Kommandozeilenargumente übergeben. /** * This class will represent the start point for the console application. * We are using the MVC-Pattern and first generate a singleton model and then * pass it to the main controller. * * <pre> * +------------+ * | Model | * +------------+ * /\ . /\ * / . \ * / . \ * / . \ * / \/ \ * +------------+ <------ +------------+ * | View | | Controller | * +------------+ ......> +------------+ * </pre> * * @author CodePlanet * @version 1.0.0, 04/06/2009 * @see <a href="http://www.codeplanet.eu/">http://www.codeplanet.eu/</a> */ public class Game { public Game(final String args[]) { // The preferred way to transfer control and begin working with Swing // is to use invokeLater. Runnable init = new Runnable() { @Override public void run() { // Instantiate the model. final BattleshipModel model = BattleshipModel.getInstance(); // Initialize model. if( model.initialize() == false ) { System.exit( -1 ); // Major failure, shutdown } // In this line, the view gets instantiated by the controller, too. final ControllerInterface controller = new BattleshipController( model, args ); } }; java.awt.EventQueue.invokeLater( init ); } } Mithilfe der Kommandozeilenargumente kann das Spiel über die Konsole gestartet werden. Dazu wird die folgende Syntax verwendet. java -splash:null -Dfile.encoding=cp850 -jar "C:\Battleship.jar" console Das Spiel kann durch den Doppelkick auf das Jar-Archiv auch automatisch gestartet werden. Allerdings startet es in diesem Fall direkt im Modus GUI mit grafischer Benutzeroberfläche. JavadocFür das gesamte Projekt Battleship steht eine ausführliche Dokumentation zur Verfügung, die mit Javadoc generiert wurde. Javadoc ist ein Software-Dokumentationswerkzeug, das aus Java-Quelltexten automatisch HTML-Dokumentationsdateien erstellt. Die Daten für Battleship stehen unter http://codeplanet.eu/files/battleship/javadoc/ zur Ansicht bereit. Sie können sich die HTML-Dokumentationsdateien näher ansehen. Dort erfahren Sie mehr über den Aufbau der Pakete und Klassen. Sie erhalten einen Überblick über die Methoden einer Klasse und ihren Zweck. In der nachfolgenden Abbildung wurde das komplette Projekt mit einem Tool gemessen. Es zeigt die totale Anzahl an Klassen, Kommentarzeilen und Quelltextzeilen. ![]() Abbildung 3: Messung der »Source lines of code (SLOC)« SchlussWir sind am Ende unseres Artikels angekommen. Sie haben Methoden und Wege kennengelernt, wie sich kleine Softwareprojekte planen und durchführen lassen. Sie haben wichtige Entwurfs- und Architekturmuster kennengelernt, eine Netzwerkbibliothek programmiert und sich mit einigen interessanten Algorithmen vertraut gemacht. Dieser Artikel wurde fertiggestellt, als mit Version 1.0.0 die erste Betaversion von Battleship herausgegeben wurde. Die Betaversion ist bereits in großen Teilen einsetzbar. Einige Funktionen wurden in dieser Version allerdings noch nicht eingeführt und sind für spätere Versionen geplant. Dies betrifft nicht alle in diesem Tutorial gestellten Anforderungen. Diese sind bereits vollständig implementiert und funktionsfähig. Das Projekt Battleship enthält noch viele weitere interessante Klassen, die Sie sich im Anhang näher ansehen können. Im Quelltext erfahren Sie wie Dokumente für den Chat serialisiert werden, bevor man sie über ein Netzwerk überträgt, wie sich Popupfenster in Java erstellen lassen, wie man seine eigenen Renderer programmiert und wie Ressourcen verwaltet werden. Darüberhinaus finden Sie viele nützliche Funktionen, die Sie in ihren Java-Programmen weiterverwenden können. Neben der sehr flexiblem Netzwerkbibliothek sind das auch kurze Codesnippets, z.B. zur Erzeugung von MD5- oder SHA-Hashwerten oder Methoden zur Manipulation von Bits und Bytes. Wir hoffen Sie hatten Spaß beim Lesen dieses Artikels, haben einige neue Informationen gewinnen können und wünschen Ihnen viel Spass beim Nachprogrammieren. Ihr CodePlanet Team. |
|||||||||||
Zuletzt aktualisiert am Montag, den 23. April 2012 um 12:59 Uhr |
AUSWAHLMENÜ | ||||||||
|