So I am trying to create a class that represents a fictional Licence Number for a given person. The Licence Number is constructed from the following:
So for example, Maggie Smith whose licence was issued in 1990, might have a Licence Number of MS-1990-11, with 11 being the serial number. However, Mark Sanders might have had his licence issued the same year meaning the beginning of his licence may be MS-1990 also.
So this is where the problem occurs. I need to make sure that the serial number for this person is not the same as Maggie's. So I'd have to check through any records that have the same initials and issue year, then generate a new unique serial number. Here's my code so far:
public class LicenceNumber {
private final Name driverName;
private final Date issueDate;
private static final Map<String, LicenceNumber> LICENCENOS = new HashMap<String, LicenceNumber>();
public LicenceNumber(Name driverName, Date issueDate){
this.driverName = driverName;
this.issueDate = issueDate;
}
public static LicenceNumber getInstance(Name driverName, Date issueDate){
Calendar tempCal = Calendar.getInstance();
tempCal.setTime(issueDate);
String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));
int serialNo = 1;
String k = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1) + "-" + issueYear + "-" + serialNo;
if(!LICENCENOS.containsKey(k)){
LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
}
return LICENCENOS.get(k);
}
public boolean isUnique(){
return true;
}
public Name getDriverName() {
return driverName;
}
public Date getIssueDate() {
return issueDate;
}
}
And a snippet of how it may be instantiated:
public final class DrivingLicence {
private final Name driverName;
private final Date driverDOB;
private final Date issueDate;
private final LicenceNumber licenceNo;
private final boolean isFull;
public DrivingLicence(Name driverName, Date driverDOB, Date issueDate, boolean isFull){
//TO-DO validate inputs
this.driverName = driverName;
this.driverDOB = driverDOB;
this.issueDate = issueDate;
this.licenceNo = LicenceNumber.getInstance(driverName, issueDate);
//this.licenceNo = new LicenceNumber(driverName, issueDate);//instantiate a licence number using the driverName and dateOfIssue
this.isFull = isFull;
}
}
I've based it on some lecture notes that discuss how you can use factories for uniqueness. I'm also unsure if I should create a LicenceNumber using getInstance or by creating a new Object. Does anyone know a way I could check if a serial number for a given string, e.g. XX-XXXX already exists?
Here is an approach that will create incremental numbers for the duration of the program. Since there is no backing database, each run of the program will reset.
It works by using an AtomicInteger
to ensure uniqueness. I have used a ConcurrentMap
to take advantage of thread safety as well as the .putIfAbsent
method. However, it can be easily converted to use a standard Map
. I also just used a String
, but a better approach would be to use a real domain object. It is sufficient for handling the OP's question and for illustrative purposes.
// a Map for holding the sequencing
private ConcurrentMap<String, AtomicInteger> _sequence =
new ConcurrentHashMap<>();
/**
* Returns a unique, incrementing sequence, formatted to
* 0 prefixed, 3 places, based upon the User's initials
* and the registration year
*/
public String getSequence(String initials, String year)
{
String key = makePrefix(initials, year);
AtomicInteger chk = new AtomicInteger(0);
AtomicInteger ai = _sequence.putIfAbsent(key, chk);
if (ai == null) {
ai = chk;
}
int val = ai.incrementAndGet();
String fmt = String.format("%03d", val);
return fmt;
}
/**
* A helper method to make the prefix, which is the
* concatintion of the initials, a "-", and a year.
*/
private String makePrefix (String initials, String year)
{
return initials + "-" + year;
}
Test Example:
public static void main(String[] args)
{
LicensePlate_37169055 lp = new LicensePlate_37169055();
System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
System.out.println("ms, 1999: " + lp.getSequence("ms", "1999"));
System.out.println("ko, 2001: " + lp.getSequence("ko", "2001"));
}
Example Output:
ko, 1999: 001
ac, 1999: 001
ko, 1999: 002
ac, 1999: 002
ms, 1999: 001
ko, 2001: 001
To integrate into the OP's code, the following modifications are suggestive:
public static LicenceNumber getInstance(Name driverName, Date issueDate){
Calendar tempCal = Calendar.getInstance();
tempCal.setTime(issueDate);
String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));
// ** get the initials; I would actually move this functionality to be
// a method on the Name class
String initials = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1);
// get the unique serial number
String serial = getSequence(initials, issueYear);
// make the full licenseplate String
String k = makePrefix(initials, issueYear) + "-" + serial;
if(!LICENCENOS.containsKey(k)){
LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
}
return LICENCENOS.get(k);
}