Last active
March 12, 2020 19:55
-
-
Save oschmid/3ee4c21525ef9082390ba469c897d7cf to your computer and use it in GitHub Desktop.
To Do List Design Exercise
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Task { | |
public Status status; | |
public String description; | |
public Optional<Integer> priority; | |
public Visibility visibility; | |
} | |
public enum Status { | |
TODO, IN_PROGRESS, UNDER_REVIEW, BLOCKED, DONE | |
} | |
public enum Visibility { | |
PRIVATE, FRIENDS | |
} | |
public class ToDoList { | |
private final UserId owner; | |
private final List<Task> tasks; | |
private final Set<UserId> friends; | |
private final List<Change> history; | |
// includes private as well as public | |
public int getTotal(); | |
// returns tasks visible to user | |
public ImmutableList<Task> getTasks(UserId user); | |
// rewinds change history and returns tasks visible to the user | |
public ImmutableList<Task> getTasksSnapshot(Instant instant, UserId user); | |
public void add(Task task, UserId user); | |
public void update(Task oldTask, Task newTask, UserId user); | |
public void delete(Task task, UserId user); | |
} | |
public interface Change { | |
public Instant getTimestamp(); | |
public ImmutableList<Task> undo(ImmutableList<Task> tasks); | |
} | |
public class Add implements Change; | |
public class Update implements Change; | |
public class Delete implements Change; | |
// getTasks() can be filtered with: | |
public Predicate<Task> onlyState(State state); | |
// getTasks() can be sorted with: | |
public Comparator<Task> byPriority(); | |
public class UserId { | |
private final UUID uuid; | |
} | |
public class User { | |
public final UserId id; | |
public String username; | |
public final ToDoList todoList; | |
} | |
public class App { | |
private final Map<UserId, User> users; | |
private final Map<String, UserId> usernames; | |
User login(String username, String password); | |
User getUser(UserId userId); | |
User getUser(String username); | |
Integer getCountOfUsersWithTasksMatching(String description); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Initial Design
Pretty straight forward. I chose to use "TODO" instead of "not done" as names containing negation can be confusing. Tasks are immutable so instead of updating in place, changes (such as marking items as done) are made by replacement using
update(old, new)
.First round of feature requests
Added more values to the
State
enum. One alternative I considered was to makeState
a class and allow custom states perToDoList
. But this seemed like overkill. Also added optional priorities. The UI can filter and sort to-do items by callingToDoList.getTasks()
and then using Java streams along with theonlyState()
Filter
andbyPriority()
Comparator
to filter and sort tasks before displaying them.Second round of feature requests
Added a task
Visibility
and updated the methods ofToDoList
to require theUserID
of the user making each change or viewing the list of tasks. Internally it'll compare thisUserID
to the owner and set of friends to determine what to show. Since we now have users, I added anApp
class to handle login and looking up theToDoList
of a friend. Currently the user would have to know their friend'sUUID
to add them or look them up but adding usernames and a lookup by name is pretty straight forward.UUID
is preferable to just using aString
since the user may want to change their username.Third round of feature requests
Counting the users with the same task descriptions is a strange (and insecure) feature but easy enough to add to
App
. I forgot to add it but we can have a list of Users as our internal data structure. The spec says to count the tasks of all users so I'm assuming private tasks are counted too. Though this may change to include only our friends or only public tasks. If so,getCountOfUsersWithTasksMatching()
may change and include aUserId
param.Because of my decision to return an
ImmutableList<Task>
rather than allow editing in place, adding change history is straight forward.add()
,update()
, anddelete()
can add concrete implementations of theChange
interface to the change history list andgetTasksSnapshot()
can rewind that list until the desiredInstant
in time. There's no way of retroactively adding history toToDoList
s that already exist so the earliest snapshot will just be the list as it existed when the history feature was introduced.(Final cleanup)
I changed the name of
State
toStatus
since it fits better, added a missinggetTimestamp()
method to theChange
interface. And a map to store users and usernames inApp
.