Skip to content

Instantly share code, notes, and snippets.

@kietdo360
Created May 8, 2024 10:53
Show Gist options
  • Save kietdo360/bb3b66771434d3859fb9284199054728 to your computer and use it in GitHub Desktop.
Save kietdo360/bb3b66771434d3859fb9284199054728 to your computer and use it in GitHub Desktop.
Java coding challenge
package com.examplefoobar.utils;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Closeable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashSet;
/**
* FIXME: This class was written by a really junior software engineer.
* It seems there are a lot of rooms for improvement in this class.
* Please give us your feedback about this class (with suggestions if possible.)
*/
/**
* A thread-safe container that stores a group ID and members.
*
* This class can store <tt>Member</tt> and/or <tt>AdminMember</tt>.
* Also, it can start and stop a background task that writes a member list to specified 2 files.
*
* This class is called many times, so it should have a good performance.
*
* Example usage:
*
* DisappointingGroup group = new DisappointingGroup("group-001");
*
* group.addMember(new DisappointingGroup.Member("member-100", 42));
* group.addMember(new DisappointingGroup.AdminMember("admin-999", 30));
* group.addMember(new DisappointingGroup.Member("member-321", 15));
*
* group.startLoggingMemberList10Times("/tmp/output.primary", "/tmp/output.secondary");
*/
@ThreadSafe
public class DisappointingGroup
implements Closeable
{
String groupId;
HashSet<Member> members;
boolean isRunning;
boolean shouldStop;
class Member
{
String memberId;
int age;
Member(String memberId, int age)
{
this.memberId = memberId;
this.age = age;
}
public String getMemberId()
{
return memberId;
}
public int getAge()
{
return age;
}
public boolean equals(Object o)
{
// If `memberId` matches the other's one, they should be treated as the same `Member` objects.
Member member = (Member) o;
return this.memberId == member.memberId;
}
}
class AdminMember extends Member
{
AdminMember(String memberId, int age)
{
super(memberId, age);
}
}
public DisappointingGroup(String groupId)
{
this.groupId = groupId;
this.members = new HashSet<>();
}
public void addMember(Member member)
{
members.add(member);
}
private String getDecoratedMemberId(Member member)
{
if (member instanceof Member) {
return member.getMemberId() + "(normal)";
}
else if (member instanceof AdminMember) {
return member.getMemberId() + "(admin)";
}
return null;
}
private String getMembersAsStringFlooringAge()
{
String buf = "";
for (Member member : members)
{
// Floor the age: e.g. 37 -> 30
Integer flooredAge = (member.getAge() / 10) * 10;
String decoratedMemberId = getDecoratedMemberId(member);
buf += String.format("memberId=%s, age=%d¥n", decoratedMemberId, flooredAge);
}
return buf;
}
@Override
public void close()
throws IOException
{
}
/**
* Run a background task that writes a member list to specified files 10 times in background thread
* so that it doesn't block the caller's thread.
*
* Only one thread is allowed to run at once
* - When this method is called and another thread is running, the method call should just return w/o starting any thread
* - When this method is called and another thread is already finished, the method call should start a new thread
*/
public void startLoggingMemberList10Times(final String outputFilePrimary, final String outputFileSecondary)
{
// Only one thread is allowed to run at once
if (isRunning) {
return;
}
isRunning = true;
new Thread(new Runnable() {
@Override
public void run()
{
int i = 0;
while (!shouldStop)
{
if (i++ >= 10)
break;
FileWriter writer0 = null;
FileWriter writer1 = null;
try {
String membersStr = DisappointingGroup.this.getMembersAsStringFlooringAge();
writer0 = new FileWriter(new File(outputFilePrimary));
writer0.append(membersStr);
writer1 = new FileWriter(new File(outputFileSecondary));
writer1.append(membersStr);
}
catch (Exception e) {
throw new RuntimeException(
"Unexpected error occurred. Please check these file names. outputFilePrimary="
+ outputFilePrimary + ", outputFileSecondary=" + outputFileSecondary);
}
finally {
try {
if (writer0 != null)
writer0.close();
if (writer1 != null)
writer1.close();
}
catch (Exception ignored) {
// Do nothing since there isn't anything we can do here, right?
}
}
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* Stop the background task started by <tt>startLoggingMemberList10Times()</tt>
*/
public void stopPrintingMemberList()
{
shouldStop = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment