I am creating a planner/scheduling app using objects. I am just a beginner with object oriented programming, so any tips on how to better structure my classes would be very helpful.
Program Basics
The program consists of "objectives" and "checkpoints." An objective is essentially a goal, and checkpoints are smaller steps that must be completed to reach that goal. Objectives and checkpoints all have "deadlines" (due dates) and are part of the calendar object. Eventually, I'd like to make a calendar display that shows the objectives and checkpoints.
Code
// GUID generator
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Adds add day feature to Date prototype
Date.prototype.addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
}
// Parent object for all data
var calendar = {
// Contains all objectives
objectives: new Array(),
// Checkpoints must be completed in order?
strict: true,
// Create objective, add to list, and sort list by deadline
addObjective: function(name, deadline, description) {
this.objectives.push(new Objective(name, deadline, description));
this.sortObjectives();
},
getObjectives: function() {
return this.objectives;
},
setStrict: function(strict) {
this.strict = strict;
},
// Sort objectives by deadline, most recent last
sortObjectives() {
this.objectives.sort((a, b) => (a.deadline > b.deadline) ? 1 : -1);
}
}
class Objective {
constructor(name, deadline, description) {
this.name = name;
this.deadline = new Date(deadline);
this.description = description;
this.checkpoints = new Array();
this.status = false;
this.id = uuidv4();
}
// Push back deadline
moveDeadline(days=0, moveAll=false) {
this.deadline = this.deadline.addDays(days)
// Move all checkpoints after current date unless incomplete
if (moveAll) {
let today = new Date();
for (let i = 0; i < this.checkpoints.length; i++) {
if (this.checkpoints[i].deadline < today || !this.checkpoints[i].status) {
this.checkpoints[i].deadline.addDays(days);
}
}
}
}
// Remove objective from calendar object
delete() {
calendar.objectives.splice(calendar.objectives.indexOf(this), 1);
}
// Create checkpoint, add to list, sort order by deadline
addCheckpoint(name, deadline, description) {
this.checkpoints.push(new Checkpoint(name, deadline, description, this.id));
this.sortCheckpoints();
}
getName() {
return this.name;
}
setName(name) {
this.name = name;
}
getDeadline() {
return this.deadline.toDateString();
}
setDeadline(deadline) {
this.deadline = new Date(deadline);
}
getDescription() {
return this.description;
}
setDescription(description) {
this.description = description;
}
getStatus() {
return this.status;
}
// Checks whether all checkpoints are complete and determines status, invoked by Checkpoint.prototype.setStatus()
setStatus() {
let checkpoints = this.getCheckpoints();
let allComplete = true;
// Iterates through checkpoints
for (let i = 0; i < checkpoints.length; i++) {
// If at least one checkpoint is incomplete, objective status is false
if (!checkpoints[i].status) {
allComplete = false;
break;
}
}
if (allComplete) {
this.status = true;
}
else {
this.status = false;
}
}
getCheckpoints() {
return this.checkpoints;
}
getid() {
return this.id;
}
// Sort checkpoints by deadline, most recent last
sortCheckpoints() {
this.checkpoints.sort((a, b) => (a.deadline > b.deadline) ? 1 : -1);
}
}
class Checkpoint {
constructor(name, deadline, description, id) {
this.name = name;
this.deadline = new Date(deadline);
this.description = description;
this.status = false;
this.id = uuidv4();
this.objectiveid = id;
}
// Push back deadline
moveDeadline(days=0, moveAll=false) {
this.deadline = this.deadline.addDays(days)
// Move all checkpoints after this checkpoint deadline
if (moveAll) {
for (let i = 0; i < this.getObjective().getCheckpoints().length; i++) {
if (this.getObjective().getCheckpoints()[i].deadline <= this.deadline && this.getObjective().getCheckpoints()[i] != this) {
this.getObjective().getCheckpoints()[i].deadline.addDays(days);
}
}
}
}
// Remove checkpoint from objective object
delete() {
let objective = this.getObjective();
objective.checkpoints.splice(objective.checkpoints.indexOf(this), 1);
}
getName() {
return this.name;
}
setName(name) {
this.name = name;
}
getDeadline() {
return this.deadline.toDateString();
}
setDeadline(deadline) {
this.deadline = new Date(deadline);
}
getDescription() {
return this.description;
}
setDescription(description) {
this.description = description;
}
getStatus() {
return this.status;
}
// Update checkpoint status
setStatus(status) {
if (status == true) {
if (calendar.strict) {
let previousCheckpoints = this.getObjective().getCheckpoints().slice(0, this.getObjective().getCheckpoints().indexOf(this));
let strictCondition = true;
// Checks if all preceding checkpoints are completed if "strict" progress is enabled
for (let i = 0; i < previousCheckpoints.length; i++) {
if (!previousCheckpoints[i].status) {
strictCondition = false;
break;
}
}
if (strictCondition) {
this.status = true;
}
else {
console.log('must complete preceding checkpoints');
}
}
else {
this.status = true;
}
}
// No conditions for reverting checkpoint to incomplete
else if (status == false) {
this.status = false;
}
// Check objective status
this.getObjective().setStatus();
}
getid() {
return this.id;
}
getObjectiveid() {
return this.objectiveid;
}
// Return reference to parent objective object
getObjective() {
for (let i = 0; i < calendar.objectives.length; i++) {
let objective = calendar.objectives[i]
if (objective.getid() == this.objectiveid) {
return(objective);
}
}
}
}
```