IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Pas de synchronisation requise dans un constructeur ?


Sujet :

Langage Java

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut Pas de synchronisation requise dans un constructeur ?
    bonjour

    j'ai une classe destinée à être appelée par plusieurs threads.

    Jusqu'à présent, une méthode appelée uniquement par le constructeur mettait également en oeuvre les pré requis de la stratégie de synchronisation retenue (read write lock sur une map partagée).

    Sommes nous d'accord que cela est inutile ?

    Iriez vous jusqu'à enlever ce bout de code ?

    merci d'avance
    nono

  2. #2
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    en fait un objet n'a pas besoin d'accès "synchronized" tant qu'il n'est pas accessible.
    En principe cela explique pourquoi un constructeur n'est pas "synchronized": l'objet est en cours de construction et sa référence ne doit pas être connue d'autres threads que celui qui est en train de le créer....
    Sauf que .... si jamais pendant l'exécution du code du constructeur la référence "this" est passée à d'autres données externes (par ex. rattachement à un arbre) là il y a danger.... (genre d'erreur: dans un constructeur d'un composant graphique on rattache le composant courant en cours de création à d'autres composants ...)... il peut y avoir encore d'autres cas bien tordus

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut
    qu'en conclure donc ?

    qu'il faut tout de même synchroniser "au cas où" ? => je parle essentiellement de quelle bonne pratique à recommander

    pour ma part, ne pas faire appel à des méthodes surchargeables dans le constructeur me semble un BA BA. Aussi, j'aurai tendance à dire qu'on peut zapper la synchronization dans les constructeurs...

  4. #4
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par livenono Voir le message
    qu'il faut tout de même synchroniser "au cas où" ? => je parle essentiellement de quelle bonne pratique à recommander
    Impossible de dire cela sans voir ton code, même si en règle général les constructeurs n'utilise pas de ressources partagées...

    a++

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut
    Citation Envoyé par adiGuba Voir le message
    Salut,



    Impossible de dire cela sans voir ton code, même si en règle général les constructeurs n'utilise pas de ressources partagées...

    a++
    hum, de quoi as tu besoin exactement ?

    le code en question effectue une lecture complète d'un fichier de données afin de mettre les enregistrements s'y trouvant en mémoire (via une Map location<->enregistrement) => il fait quelques lignes..

    ceci dit, pour l'essentiel, il n'appelle que des méthodes privées. Dur donc à priori de laisser échapper this qq part...

  6. #6
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Ben que ce soit un constructeur ou une méthode, il faut vérifier la portée de tous les éléments qu'il/elle manipule (et pas seulement this) afin de s'assurer qu'ils ne peuvent pas être utilisé autre part simultanément.


    Sans voir le code source c'est difficile de le dire...

    a++

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut
    ok, voici le code en question (étant donné que c'est relatif à une cerif SCJD, je l'ai pas mal élagué pour essayer de ne laisser que ce qui compte, en cas de manque me le dire :$):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
     
    package suncertify.db.file;
     
    import java.io.UnsupportedEncodingException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    import java.util.logging.Logger;
     
    import suncertify.db.DatabaseManager;
    import suncertify.db.DbException;
    import suncertify.db.RecordNotFoundException;
    import suncertify.db.utils.ArraysUtils;
    import suncertify.db.utils.Check;
     
    /**
     * Implementation of DatabaseManager.
     * 
     * It can handle any file database having the proper magic cookie.
     * FileDatabaseManager reads the schema description and enforces it afterwards.
     * 
     * The actual file operations are handled by the FileDatabaseOperator class, in
     * order to ensure the consistent use of DbException, a generic exception type
     * to avoid file database specific exceptions, hence making future changes
     * easier.
     * 
     * The records are put in memory for faster read operations, which are expected
     * to be the most common, especially for find operations.
     * 
     * The class makes no assumption about its context and thus ensures itself the
     * whole synchronization strategy.
     * 
     * @See DatabaseManager
     * @See FileDatabaseOperator
     */
    public class FileDatabaseManager implements DatabaseManager {
     
    	/**
             * Length of the status field on the file.
             */
    	private static final int STATUS_LENGTH = 4;
     
    	/**
             * Encoding of the content of the file.
             */
    	private static final String ENCODING = "UTF";
     
    	/**
             * The magic cookie which is required in the file for it to be a proper file
             * database.
             */
    	private static final int MAGIC_COOKIE = 798;
     
    	/**
             * The logger used by the instance: all logs in the class uses it.
             */
    	private final Logger log = Logger.getLogger("suncertify.db");
     
    	/**
             * Does the physical operations on the file (read & write) with
             * synchronization handled by the current class. Wraps errors in
             * DbExceptions.
             */
    	private final FileDatabaseOperator database;
     
    	/**
             * The maximal length of a record, computed from the schema description read
             * at the beginning of the file database.
             */
    	private final int recordLength;
     
    	/**
             * List of Field describing the schema, each Field having a name and a
             * length.
             * 
             * @See Field
             */
    	private final List<Field> schema;
     
    	/**
             * The records cache. Each record is accessed by its record number. This
             * records map is populated at startup and then maintained afterwards.
             * 
             * Access to the map is guarded by the recordsLock field.
             * 
             * @See Record
             */
    	private final Map<Long, Record> records = new HashMap<Long, Record>();
     
    	/**
             * Allows multiple reads at the same time but only one write at a time,
             * hence used to ensure the records map integrity: none can modify it while
             * others are reading it, as well as only one thread can modify it a time.
             * 
             * The lock is reentrant in case the same thread requires it twice in a row.
             */
    	private final ReadWriteLock recordsLock = new ReentrantReadWriteLock();
     
    	/**
             * The record number generator. Must be used while holding the write lock on
             * the recordsLock.
             */
    	private Long recordNumberGenerator = 0L;
     
    	/**
             * The currently free locations on the file database.
             * 
             * The freeLocation queue is populated at startup and then maintained
             * afterwards. It should be accessed through the recordsLock, with a write lock.
             */
    	private final LinkedList<Long> freeLocations = new LinkedList<Long>();
     
    	/**
             * An empty record, useful when converting record's array to the
             * corresponding string to avoid rebuilding it all every time. See
             * convertToRawData for more details.
             */
    	private final String emptyRecord;
     
    	/**
             * Creates a new FileDatabaseManager from the path provided.
             * 
             * @param path
             *            path to the file database.
             * @throws DbException
             *             in case of exception during creation.
             */
    	public FileDatabaseManager(final String path) throws DbException {
    		log.entering("FileDatabaseManager", "FileDatabaseManager", path);
    		database = new FileDatabaseOperator(path);
    		// TODO : either always lock or never
    		checkMagicCookie();
    		int offset = database.readInt("offset");
    		schema = readSchema();
    		recordLength = computeRecordLength();
    		emptyRecord = new String(new byte[recordLength]);
    		populateRecords(offset);
    		log.exiting("FileDatabaseManager", "FileDatabaseManager");
    	}
     
    	/**
             * Compute the record length based on the schema read from the file.
             * Includes the status length.
             * 
             * @return The size in integers.
             */
    	private int computeRecordLength() {
    		log.entering("FileDatabaseManager", "computeRecordLength");
    		int length = 0;
    		for (final Field field : schema) {
    			length += field.getLength();
    		}
    		log.exiting("FileDatabaseManager", "computeRecordLength", length);
    		return length;
    	}
     
    	/**
             * Read the record schema from the file database.
             * 
             * @return The schema read in the form of a list of Field.
             * @See Field.
             */
    	private List<Field> readSchema() {
    		log.entering("FileDatabaseManager", "readSchema");
    		final short numberOfField = database.readShort("numberOfField");
    		final List<Field> readFields = new ArrayList<Field>(numberOfField);
    		for (int i = 0; i < numberOfField; i++) {
    			short nameLenght = database.readShort("field name length n°" + i);
    			byte[] fieldNameByteArray = new byte[nameLenght];
    			database.readBytes(fieldNameByteArray, "fieldName");
    			String fieldName;
    			try {
    				fieldName = new String(fieldNameByteArray, ENCODING);
    			} catch (UnsupportedEncodingException e) {
    				throw new DbException(
    						"Error while converting byte[] of length '"
    								+ fieldNameByteArray.length + "' and content '"
    								+ fieldNameByteArray.toString()
    								+ "' with encoding '" + ENCODING
    								+ "' when reading schema.", e);
    			}
    			short fieldLength = database.readShort("field length n°" + i);
    			readFields.add(new Field(fieldName, fieldLength));
    		}
    		log.exiting("FileDatabaseManager", "readSchema", readFields);
    		return Collections.unmodifiableList(readFields);
    	}
     
    	/**
             * Read the magic cookie and checks it has the expected value. If not,
             * throws a DbException.
             * 
             * @throws DbException
             *             if the magic cookie isn't valid
             */
     
    	private void checkMagicCookie() throws DbException {
    		log.entering("FileDatabaseManager", "checkMagicCookie");
    		int readMagicCookie = database.readInt("magicCookie");
    		if (readMagicCookie != MAGIC_COOKIE) {
    			throw new DbException(
    					"Not a data file. Expected the magic cookie '"
    							+ MAGIC_COOKIE + "', read '" + readMagicCookie
    							+ "'.");
    		}
    		log.exiting("FileDatabaseManager", "checkMagicCookie");
    	}
     
    	/**
             * Read the locations and content of each database record, putting them in
             * the records map. If a record isn't valid, populate the freeLocations
             * queue.
             * 
             * @param offset
             *            where to start in the file database.
             * 
             */
    	private void populateRecords(final int offset) {
    		log.entering("FileDatabaseManager", "populateRecords", offset);
    		recordsLock.writeLock().lock();
    		try {
    			long databaseLength = database.getDatabaseLength();
    			for (long locationInFile = offset; locationInFile < databaseLength; locationInFile += recordLength
    					+ STATUS_LENGTH) {
    				String[] rawRecord = readRawRecord(locationInFile);
    				if (rawRecord != null) {
    					records.put(recordNumberGenerator++, new Record(
    							locationInFile, rawRecord));
    				} else {
    					freeLocations.offer(locationInFile);
    				}
    			}
    		} finally {
    			recordsLock.writeLock().unlock();
    		}
    		log.exiting("FileDatabaseManager", "populateRecords");
    	}
     
     
     
    	/**
             * Checks that provided record matches the schema definition of the current
             * file.
             * 
             * @param record
             * @throws IllegalStateException
             *             if the record isn't valid
             */
    	private void checkRecord(final String[] record)
    			throws IllegalStateException {
    		log.entering("FileDatabaseManager", "checkRecord", record);
    		Check.notNull(record, "record");
    		Check.areEquals(schema.size(), record.length, "record length");
    		for (int i = 0; i < record.length; i++) {
    			String fieldContent = record[i];
    			Field fieldDescription = schema.get(i);
    			Check.notNull(fieldContent, "Content for field "
    					+ fieldDescription.getName());
    			Check
    					.isValid(fieldContent.length() <= fieldDescription
    							.getLength(), "Lenght of content for field "
    							+ fieldDescription.getName());
    		}
    	}
     
     
    	/**
             * Read the raw record content.
             * 
             * @param location
             *            in the file where to read from.
             * @return Array of String, being the raw content.
             */
    	private String[] readRawRecord(final Long location) {
    		log.entering("FileDatabaseManager", "readRawRecord", location);
    		byte[] rawRecord = new byte[recordLength];
    		boolean valid = false;
    		database.seek(location, "record to read");
    		if (isStatusValid(database.readShort("status"))) {
    			valid = true;
    			database.readFully(rawRecord, "rawRecord");
    		}
    		String[] readRecord = null;
    		if (valid) {
    			readRecord = convertBytesToRecord(rawRecord);
    		} else {
    			freeLocations.offer(location);
    		}
    		log.exiting("FileDatabaseManager", "readRecord", readRecord);
    		return readRecord;
    	}
     
     
    	/**
             * Test if the given status is valid.
             * 
             * @param status
             *            The status to check.
             * @return boolean indicating whether the status is valid or not.
             */
    	private boolean isStatusValid(final short status) {
    		log.entering("FileDatabaseManager", "isStatusValid", status);
    		return (status == 0);
    	}
     
    }
    toute remarque bienvenue

  8. #8
    Membre Expert
    Avatar de professeur shadoko
    Homme Profil pro
    retraité nostalgique Java SE
    Inscrit en
    Juillet 2006
    Messages
    1 257
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 77
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : retraité nostalgique Java SE

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 257
    Par défaut
    pas le temps de tout lire mais une remarque: il me semble que le code est pris dans une contradiction:
    - les différentes actions déclenchées par le constructeur sont isolées dans des codes distincts (les méthodes private)
    - ces méthodes ne sont pas de vraies méthodes : elles ont un ordre d'appel précis et utilisent des éléments du contexte initialisés par les méthodes précédentes et/ou le code appelant.... du coup je ne trouve pas ça clair (tous les gouts sont dans la nature et tout peut se discuter).
    Comme je n'ai pas le temps de tout analyser je me pose quand meme des questions comme: certaines de ces méthodes (population des enregistrements) sont-elles du domaine du constructeur ou des "services" rendus pas l'objet?
    j'ai des doutes...
    pour la concurrence c'est effectivement les choses "externes" qu'il faut surveiller...
    Attention: quand on est trop rusé en SCJD le correcteur ne comprend rien et marque faux! il faut vraiment que les aspects concurrence soient très documentés!
    bonne chance!

  9. #9
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Perso je ne vois pas d'éléments à synchroniser dans le constructeur de FileDatabaseManager().

    Elle n'utilise que des attributs privates qui ne peuvent donc pas avoir été exporté (dans le constructeur tout du moins), et aucun n'est exporté par les méthodes appelées, qui sont toutes privates (pas de redéfinition dans une classe fille).

    Le seul point inconnu concerne le code de la classe FileDatabaseOperator, massivement utiliser dans toutes ces méthodes.


    a++

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut
    FileDatabaseOperator pour les curieux :=)

    merci pour vos réponses, je les commente en dessous

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
     
    /**
     * 
     * An utility class for making the read/write operations on a File Database,
     * wrapping exceptions in DbException with meaningful messages.
     * 
     * Thus is handles the complexity of accessing the file and writing/reading
     * specific types on the data file.
     * 
     */
    class FileDatabaseOperator {
    	/**
             * The logger used by the instance: all logs in the class uses it.
             */
    	private final Logger log = Logger.getLogger("suncertify.db");
    	/**
             * The file is accessed through a RandomAccessFile, to be able to read/write
             * easily at any point in it.
             */
    	private RandomAccessFile database;
     
    	/**
             * Creates a new FileDatabaseOperator.
             * 
             * @param path
             *            The path of the file database.
             */
    	FileDatabaseOperator(final String path) {
    		log.entering("FileDatabaseOperator", "FileDatabaseOperator", path);
    		try {
    			File databaseFile = new File(path);
    			Check.isValid(databaseFile.exists(), "Database file with '" + path
    					+ "' exists");
    			database = new RandomAccessFile(databaseFile, "rw");
    		} catch (final FileNotFoundException e) {
    			throw new DbException("Error while creating the database", e);
    		}
    	}
     
    	/**
             * Fill the given byte array with content from the file database, starting
             * at the current location.
             * 
             * @param bytes
             *            The byte array to fill with content from the file database.
             * @param description
             *            Description of the content read (for error reporting).
             */
    	void readFully(final byte[] bytes, final String description) {
    		log.entering("FileDatabaseAccess", "readFully", new Object[] { bytes,
    				description });
    		try {
    			database.readFully(bytes);
    		} catch (IOException e) {
    			throw new DbException("Error while reading fully for "
    					+ description, e);
    		}
    	}
     
    	/**
             * Put the file pointer at the asked position.
             * 
             * @param position
             *            where to put the file pointer.
             * @param description
             *            Description of the seek reason (for error reporting).
             */
    	void seek(final Long position, final String description) {
    		log.entering("FileDatabaseAccess", "seek", position);
    		try {
    			log.info("seeking " + position);
    			database.seek(position);
    		} catch (IOException e) {
    			throw new DbException("Error while seeking position '" + position
    					+ "' for " + description, e);
    		}
    	}
     
    	/**
             * Read an int from the file database from the current position.
             * 
             * @param description
             *            Description of the action's reason (for error reporting).
             * @return the read int.
             */
    	int readInt(final String description) {
    		log.entering("FileDatabaseAccess", "readInt", description);
    		try {
    			return database.readInt();
    		} catch (final IOException e) {
    			throw new DbException("Error while reading an int for '"
    					+ description + "'.", e);
    		}
    	}
     
    	/**
             * Fill the bytes array with the content of the file database at the current
             * position.
             * 
             * @param bytes
             *            the byte array to fill.
             * @param description
             *            Description of the action's reason (for error reporting).
             */
    	void readBytes(final byte[] bytes, final String description) {
    		log.entering("FileDatabaseAccess", "readBytes", new Object[] { bytes,
    				description });
    		try {
    			database.read(bytes);
    		} catch (final IOException e) {
    			throw new DbException("Error while reading byte[] of length '"
    					+ bytes.length + "' for '" + description + "'.", e);
    		}
     
    	}
     
    	/**
             * Read a short from the file database from the current position.
             * 
             * @param description
             *            Description of the action's reason (for error reporting).
             * @return the read short.
             */
    	short readShort(final String description) {
    		log.entering("FileDatabaseAccess", "readShort", description);
    		try {
    			return database.readShort();
    		} catch (final IOException e) {
    			throw new DbException("Error while reading a short for '"
    					+ description + "'.", e);
    		}
    	}
     
    	/**
             * Returns the length of the file.
             */
    	long getDatabaseLength() {
    		log.entering("FileDatabaseAccess", "getDatabaseLength");
    		try {
    			return database.length();
    		} catch (IOException e) {
    			throw new DbException("Error while getting the database length.", e);
    		}
    	}
     
    	/**
             * Write a short in the file database at the current position.
             * 
             * @param value
             *            the value to write as a short.
             * @param description
             *            Description of the action's reason (for error reporting).
             */
    	void writeShort(final int value, final String description) {
    		log.entering("FileDatabaseAccess", "writeShort", new Object[] { value,
    				description });
    		try {
    			database.writeShort(value);
    		} catch (IOException e) {
    			throw new DbException("Error while writing a short for '"
    					+ description + "'.", e);
    		}
    	}
     
    	/**
             * Write bytes in the file database from the current position.
             * 
             * @param bytes
             *            the bytes to write.
             * @param description
             *            Description of the action's reason (for error reporting).
             */
    	void writeBytes(final byte[] bytes, final String description) {
    		log.entering("FileDatabaseAccess", "writeBytes", new Object[] { bytes,
    				description });
    		try {
    			database.write(bytes);
    		} catch (IOException e) {
    			throw new DbException("Error while writing bytes '" + bytes
    					+ "' for '" + description + "'.", e);
    		}
    	}
     
    	/**
             * Close the access to the file.
             * 
             */
    	public void close() {
    		try {
    			database.close();
    		} catch (IOException e) {
    			throw new DbException(
    					"Exception while trying to close the database file", e);
    		}
    	}
     
    	/**
             * Write the status in the file database at the current position.
             * 
             * @param valid
             *            the status to write.
             * @param description
             *            Description of the action's reason (for error reporting).
             */
    	void writeStatus(final boolean valid, final String description) {
    		log.entering("FileDatabaseAccess", "writeStatus", valid);
    		if (valid) {
    			writeShort(0, "valid status for " + description);
    		} else {
    			writeShort(0x8000, "deleted status for " + description);
    		}
    	}
    }
    NB: le logging n'est pas encore en place

    @adiGuba: nous sommes donc d'accord, cool Du moins pour le moment

    @professeur shadoko: merci pour la lecture (ou le survol, c'est déjà du temps!).

    Concernant les méthodes privées utilisées, le principe de base appliqué est de découper le code en petites méthodes qui sont explicites sur leur role. Du coup (en théorie du moins lol), pas de doute sur ce qui est fait.

    ceci dit, tu as raison: ces méthodes font parfois appel à des éléments pré initialisés de la classe, d'autres fois non, ce qui est confus. Dans l'absolu, l'idée est d'utiliser au maximum les variables d'instance pour limiter les données passées.

    Toutefois, dans le même temps:
    - si une donnée n'a aucune utilité suite au constructeur ça me fait bizarre de la mettre en variable d'instance.
    - j'aime avoir des variables final autant que possible, ce qui implique de les initialiser dans le constructeur...

    Histoire de comparer, voici le tout avec les méthodes privées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    	public FileDatabaseManager(final String path) throws DbException {
    		log.entering("FileDatabaseManager", "FileDatabaseManager", path);
    		database = new FileDatabaseOperator(path);
    		// TODO : either always lock or never
    		checkMagicCookie();
    		int offset = database.readInt("offset");
    		schema = readSchema();
    		recordLength = computeRecordLength();
    		emptyRecord = new String(new byte[recordLength]);
    		populateRecords(offset);
    		log.exiting("FileDatabaseManager", "FileDatabaseManager");
    	}
    et sans:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    	public FileDatabaseManager(final String path) throws DbException {
    		log.entering("FileDatabaseManager", "FileDatabaseManager", path);
    		database = new FileDatabaseOperator(path);
    		// TODO : either always lock or never
    		int readMagicCookie = database.readInt("magicCookie");
    		if (readMagicCookie != MAGIC_COOKIE) {
    			throw new DbException(
    					"Not a data file. Expected the magic cookie '"
    							+ MAGIC_COOKIE + "', read '" + readMagicCookie
    							+ "'.");
    		}
    		int offset = database.readInt("offset");
    		final short numberOfField = database.readShort("numberOfField");
    		final List<Field> readFields = new ArrayList<Field>(numberOfField);
    		for (int i = 0; i < numberOfField; i++) {
    			short nameLenght = database.readShort("field name length n°" + i);
    			byte[] fieldNameByteArray = new byte[nameLenght];
    			database.readBytes(fieldNameByteArray, "fieldName");
    			String fieldName;
    			try {
    				fieldName = new String(fieldNameByteArray, ENCODING);
    			} catch (UnsupportedEncodingException e) {
    				throw new DbException(
    						"Error while converting byte[] of length '"
    								+ fieldNameByteArray.length + "' and content '"
    								+ fieldNameByteArray.toString()
    								+ "' with encoding '" + ENCODING
    								+ "' when reading schema.", e);
    			}
    			short fieldLength = database.readShort("field length n°" + i);
    			readFields.add(new Field(fieldName, fieldLength));
    		}
    		schema = Collections.unmodifiableList(readFields);
    		int length = 0;
    		for (final Field field : schema) {
    			length += field.getLength();
    		}
    		recordLength = length;
    		emptyRecord = new String(new byte[recordLength]);
    		long databaseLength = database.getDatabaseLength();
    		for (long locationInFile = offset; locationInFile < databaseLength; locationInFile += recordLength
    				+ STATUS_LENGTH) {
    			String[] rawRecord = readRawRecord(locationInFile);
    			if (rawRecord != null) {
    				records.put(recordNumberGenerator++, new Record(locationInFile,
    						rawRecord));
    			} else {
    				freeLocations.offer(locationInFile);
    			}
    		}
    		log.exiting("FileDatabaseManager", "FileDatabaseManager");
    	}
    je ne pense pas que ce soit plus lisible

    au final, la seule réelle option dans ce cas là (voir dans le précédent également) serait de passer par une méthode "init()" afin de décharger en tout ou partie le constructeur. Mais là adieu aux variables d'instances finales. Sans compter que quelqu'un doit alors penser à appeler l'init et ainsi de suite.. po tip top AMHA

    si jamais vous avez des suggestions ou meilleures/autres idées, je suis plus que preneur !

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 38
    Par défaut
    Citation Envoyé par professeur shadoko Voir le message
    Attention: quand on est trop rusé en SCJD le correcteur ne comprend rien et marque faux! il faut vraiment que les aspects concurrence soient très documentés!
    bonne chance!
    arf... heureux ceci dit d'avoir finalement retenu une solution plus simple que celle envisagée initialement... => mon approche me semble relativement "simple" au final non ? Pour l'aspect documentation, je pensais réserver le plus gros de la chose dans le fichier texte joint et non dans la javadoc, est ce une bonne stratégie ?

Discussions similaires

  1. Réponses: 1
    Dernier message: 09/07/2013, 00h45
  2. Réponses: 2
    Dernier message: 27/05/2013, 12h03
  3. Allocation de pointeur dans le constructeur ou pas ?
    Par Beulard dans le forum Langage
    Réponses: 8
    Dernier message: 04/12/2010, 17h00
  4. Réponses: 6
    Dernier message: 24/11/2006, 13h21
  5. Capture d'exception dans un constructeur
    Par declencher dans le forum Composants VCL
    Réponses: 8
    Dernier message: 03/02/2004, 13h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo