I am learning Java and hence practicing it from here. The solution seems pretty trivial to me but still I would like to have a honest review which would guide me to following ideas:
- Am I maintaining class Invariants?
- Do I need to have more Domain specific error messages?
- Although the solution seems trivial but I am missing some performance boost?
- Am I following proper encapsulation?
- Using correct data structures?
- Any other suggestion are most welcome.
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
public class DNA {
private final Map<Character, Integer> nucleotideMap = new HashMap<>();
private final String dnaSequence;
public DNA(String dnaSequence) {
this.dnaSequence = dnaSequence;
initializeDefaultNucleotideMap();
if (!dnaSequence.isEmpty()) {
countNucleotides();
}
}
private Map<Character, Integer> initializeDefaultNucleotideMap() {
nucleotideMap.put('A', 0);
nucleotideMap.put('C', 0);
nucleotideMap.put('G', 0);
nucleotideMap.put('T', 0);
return nucleotideMap;
}
private void countNucleotides() {
for (Character chr : dnaSequence.toCharArray()) {
nucleotideMap.put(chr, nucleotideMap.get(chr) + 1);
}
}
public int count(char nucleotide) {
if (!nucleotideMap.containsKey(nucleotide)) {
throw new IllegalArgumentException("Invalid nucleotide");
}
return nucleotideMap.get(nucleotide);
}
public Map<Character, Integer> nucleotideCounts() {
return Collections.unmodifiableMap(nucleotideMap);
}
}
Test suites:
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import org.junit.Test;
public class NucleotideTest {
@Test
public void testEmptyDnaStringHasNoAdenosine() {
DNA dna = new DNA("");
assertThat(dna.count('A')).isEqualTo(0);
}
@Test
public void testEmptyDnaStringHasNoNucleotides() {
DNA dna = new DNA("");
assertThat(dna.nucleotideCounts()).hasSize(4).contains(
entry('A', 0),
entry('C', 0),
entry('G', 0),
entry('T', 0)
);
}
@Test
public void testRepetitiveCytidineGetsCounted() {
DNA dna = new DNA("CCCCC");
assertThat(dna.count('C')).isEqualTo(5);
}
@Test
public void testRepetitiveSequenceWithOnlyGuanosine() {
DNA dna = new DNA("GGGGGGGG");
assertThat(dna.nucleotideCounts()).hasSize(4).contains(
entry('A', 0),
entry('C', 0),
entry('G', 8),
entry('T', 0)
);
}
@Test
public void testCountsOnlyThymidine() {
DNA dna = new DNA("GGGGGTAACCCGG");
assertThat(dna.count('T')).isEqualTo(1);
}
@Test
public void testCountsANucleotideOnlyOnce() {
DNA dna = new DNA("CGATTGGG");
dna.count('T');
assertThat(dna.count('T')).isEqualTo(2);
}
@Test
public void testDnaCountsDoNotChangeAfterCountingAdenosine() {
DNA dna = new DNA("GATTACA");
dna.count('A');
assertThat(dna.nucleotideCounts()).hasSize(4).contains(
entry('A', 3),
entry('C', 1),
entry('G', 1),
entry('T', 2)
);
}
@Test(expected = IllegalArgumentException.class)
public void testValidatesNucleotides() {
DNA dna = new DNA("GACT");
dna.count('X');
}
@Test
public void testCountsAllNucleotides() {
String s = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC";
DNA dna = new DNA(s);
assertThat(dna.nucleotideCounts()).hasSize(4).contains(
entry('A', 20),
entry('C', 12),
entry('G', 17),
entry('T', 21)
);
}
}