La mancanza di "esecuzione del contratto" indica un problema di progettazione fondamentale
Come @docbrown mi oppongo al "fallire veloce come la migliore idea". L'approccio "non fare nulla" è abbastanza fattibile, e preferito , quando abbiamo un progetto coerente.
Your question title starts with "Breaking the contract" - but what is the contract of addScore?
Non c'è un contratto perché c'è un grosso buco nel design.
PlayerCollection
vice List<Player>
Una raccolta di giocatori definirà e farà rispettare questo contratto sfuggente; senza dipendere da qualche benevolo codice client per farlo per noi.
public class PlayerCollection {
protected List<Player> players;
// Here, we begin to enforce the contract.
// not inheriting. Hiding List<T> methods we don't allow clients
// to do anything except what we allow.
public void addPlayer(Player newPlayer) {}
// null guard in here ==> enforcing that contract.
// if null, instantiate a new Player - if player has a default constructor.
// if duplicate, ignore - if Player overrides equals()
public void addScore(Player thisPlayer, int score) {}
// null guard here. I like do nothing, vice an exception.
// design decision time: if player not in the collection then do we add it? Your choice.
// make clients to use the API. Make them call 'addPlayer'.
// I see a null player and adding zero as the same thing. So I
// say "do nothing" when null.
// adding a new player in addScore is a violation of single
// responsibility principle IMHO.
}
"Player" fa la sua parte di applicazione del contratto
public class Player {
public Player( parameterListHere ) {
// enforcing the contract here.
// guard for nulls.
// guard for incompatible or invalid data
// default and named parameters go a long way toward
// dynamically setting complete, valid state in the constructor.
}
// design decision: is a "raw" player object allowed? i.e. can we do without
// name, team, etc. to start with? This drives whether there is a public
// default constructor and an equals override. All "null" player objects
// would be equal to each other.
// either way, the player collection will enforce the contract,
// "contains()" just behaves differently.
public Player() {
// enforcing the contract here too!
// if there is a valid default state, a zero score for example,
// then the collection's addPlayer can enforce the contract.
}
public override Equals {}
// enforcing the contract.
// maybe name, team, jersey number, etc. define what equals means
}
Il contratto è già applicato quando arriviamo alla classe Event
public class Event {
private PlayerCollection players;
//private Map<Player, Integer> score;
public void addScore(Player p, int playerScore) { players.addScore(p, playerScore); }
Sopra è ciò che intendo per design coerente. Classi in cui la Single Responsibility è il principio primario in gioco. Quando composto - Event
ha un PlayerCollection
che ha Player
s - queste classi creano una sinergia di "applicazione del contratto"