Search code examples
javauniquefactory

How can you create a unique serial number for a given string?


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:

  • The initials of the person's forename and surname
  • The year the licence was issued
  • A random arbitrary serial number

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?


Solution

  • 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);
    

    }