Prolog
Alkisah anda sebagai seorang backend engineer dihadapkan kepada sebuah tugas untuk menambahkan fitur notifikasi peringatan kepada seluruh level pegawai dengan ketentuan yang bervariasi. Ketentuan yang dimaksud antara lain;
- Staf
- Staf yang memiliki memiliki KPI dibawah 30% akan menerima notifikasi "peringatan-keras".
- Staf yang memiliki KPI dibawah 67% dan diatas 30% akan menerima notifikasi "peringatan".
- Supervisor
- Supervisor yang memiliki memiliki KPI dibawah 30% akan menerima notifikasi "peringatan-keras".
- Supervisor yang memiliki masa kerja di atas 10 tahun dan memiliki KPI diatas 30% dan dibawah 60% akan menerima notifikasi "peringatan".
- Supervisor memiliki masa kerja di bawah 10 tahun dan memiliki KPI diatas 30% dan dibawah 70% akan menerima notifikasi "peringatan".
- Manajer
- Manajer yang memiliki memiliki KPI dibawah 40% akan menerima notifikasi "peringatan-keras".
- Manajer yang memiliki usia di atas 40 tahun dan memiliki KPI diatas 40% dan dibawah 60% akan menerima notifikasi "peringatan".
- Manajer yang memiliki usia di bawah 40 tahun dan memiliki KPI diatas 40% dan dibawah 70% akan menerima notifikasi "peringatan".
Aplikasi anda saat ini dibuat menggunakan bahasa pemrograman go dan hanya memiliki 4 kelas yang membedakan setiap level pegawai yang ada. Tugas anda adalah menambahkan fungsionalitas sesuai dengan ketentuan tersebut ke sources code yang ada.
Ide yang Tertolak
Anda berdiskusi dengan atasan anda dan menyampaikan ide bahwa anda akan menambahkan sebuah fungsi sendWarningNotification pada objek Staf, Supervisor dan Manager. Atasan anda mengerenyutkan dahi dan bertanya kepada anda.
Apakah pegawai di perusahaan kita sekarang berperan sebagai seorang pemberi peringatan ?
Diskusi berlanjut dan atasan anda menolak ide anda karena dianggap menambahkan sifat "alien" ke ketiga objek tersebut. Atasan anda juga men-changelle bagaimana ide tersebut nantinya akan mampu mengakomodasi apabila ada penambahan behaviour lagi terhadap ketiga objek ini.
Visitor Pattern
Pada akhirnya anda dan atasan memutuskan untuk menggunakan visitor pattern untuk menangani kasus ini.
Seluruh logika pengiriman notifikasi peringatan akan menjadi tanggung jawab dari objek PerformanceWarningVisitor. Setiap objek turunan employee(Staff, Supervisor dan Manager) tidak perlu tahu definisi dan implementasi pengiriman notifikasi peringatan. Objek Staff, Supervisor dan Manager hanya perlu memanggil method Accept dengan paramater PerformanceWarningVisitor untuk mengirimkan notifikasi peringatan kepada seluruh level pegawai.
main.go
func main() {
employees := make([]entities.Employee, 0)
performanceNotification := visitor.PerformanceWarningVisitor{}
employees = append(employees,
entities.Staff{
Id: 12343,
Name: "Pedro Pacquita",
JoinDate: time.Date(2011, time.February, 10, 0, 0, 0, 0, time.UTC),
BirthDate: time.Date(1990, time.January, 07, 0, 0, 0, 0, time.UTC),
PerformancePercentage: 47.5,
},
entities.Supervisor{
Id: 12342,
Name: "Omar Maulo Atarez",
JoinDate: time.Date(2005, time.December, 15, 0, 0, 0, 0, time.UTC),
BirthDate: time.Date(1987, time.December, 02, 0, 0, 0, 0, time.UTC),
PerformancePercentage: 75,
},
entities.Manager{
Id: 12312,
Name: "Zakaria Owele",
JoinDate: time.Date(1993, time.December, 04, 0, 0, 0, 0, time.UTC),
BirthDate: time.Date(1971, time.July, 15, 0, 0, 0, 0, time.UTC),
PerformancePercentage: 50,
},
)
for _, employee := range employees {
// send performance notification
employee.Accept(performanceNotification)
}
}
staff.go
type Staff struct {
Id int
Name string
JoinDate time.Time
BirthDate time.Time
PerformancePercentage float32
}
func (staff Staff) Accept(v Visitor) {
v.VisitStaff(&staff)
}
supervisor.go
type Supervisor struct {
Id int
Name string
JoinDate time.Time
BirthDate time.Time
PerformancePercentage float32
}
func (spv Supervisor) Accept(visitor Visitor) {
visitor.VisitSupervisor(&spv)
}
manager.go
type Manager struct {
Id int
Name string
JoinDate time.Time
BirthDate time.Time
PerformancePercentage float32
}
func (manager Manager) Accept(visitor Visitor) {
visitor.VisitManager(&manager)
}
performance_warning_visitor.go
type PerformanceWarningVisitor struct{}
func (visitor PerformanceWarningVisitor) VisitStaff(staff *entities.Staff) (interface{}, error) {
var warningType string
if staff.PerformancePercentage < 30 {
warningType = STERN_WARNING
}
if staff.PerformancePercentage >= 30 && staff.PerformancePercentage < 67 {
warningType = WARNING
}
if warningType != "" {
fmt.Printf("Notification %s send to staff : %s \n", warningType, staff.Name)
}
return warningType, nil
}
func (visitor PerformanceWarningVisitor) VisitSupervisor(spv *entities.Supervisor) (interface{}, error) {
var warningType string
durationInYear := (time.Now()).Sub(spv.JoinDate).Hours() / 8760
if spv.PerformancePercentage < 30 {
warningType = STERN_WARNING
}
if durationInYear >= 10 && (spv.PerformancePercentage >= 30 && spv.PerformancePercentage < 60) {
warningType = WARNING
}
if durationInYear < 10 && (spv.PerformancePercentage >= 30 && spv.PerformancePercentage < 70) {
warningType = WARNING
}
if warningType != "" {
fmt.Printf("Notification %s send to supervisor : %s \n", warningType, spv.Name)
}
return warningType, nil
}
func (visitor PerformanceWarningVisitor) VisitManager(manager *entities.Manager) (interface{}, error) {
var warningType string
Age := (time.Now()).Sub(manager.BirthDate).Hours() / 8760
if manager.PerformancePercentage < 40 {
warningType = STERN_WARNING
}
if Age >= 40 && (manager.PerformancePercentage >= 40 && manager.PerformancePercentage < 60) {
warningType = WARNING
}
if Age < 40 && (manager.PerformancePercentage >= 40 && manager.PerformancePercentage < 70) {
warningType = WARNING
}
if warningType != "" {
fmt.Printf("Notification %s send to manager : %s \n", warningType, manager.Name)
}
return warningType, nil
}
Perubahan adalah Kepastian
Seiring berjalannya waktu prediksi atasan anda akan adanya penambahan fitur yang melibatkan ketiga objek employee ini ternyata menjadi kenyataan.
Anda kembali diminta untuk menambahkan fitur untuk menghitung bonus pendapatan tambahan dari masing-masing level employee dengan ketentuan sebagai berikut ;
- Staf
- Setiap staf akan menerima bonus pendapatan tambahan sebesar 1000000 apabila mempeeoleh KPI di atas 87%
- Supervisor
- Setiap supervisor akan menerima bonus pendapatan tambahan sebesar 2000000 apabila mempeeoleh KPI di atas 85%
- Manajer
- Setiap manajer akan menerima bonus pendapatan tambahan sebesar 5000000 apabila mempeeoleh KPI di atas 80%
Anda dan atasan tahu bahwa sources code yang ada bisa di expand dengan mengikuti pola visitor pattern yang sudah ada.
Anda hanya perlu memanggil kembali method Accept akan tetapi kali ini gunakan BonusIncomeVisitor sebagai parameter nya
for _, employee := range employees {
// send performance notification
employee.Accept(performanceNotification)
// calculate bonus income
employee.Accept(bonusIncome)
}
Epilog
Seluruh sources code yang berkaitan dengan artikel ini dapat dilihat pada repository berikut https://github.com/Mhakimamransyah/practice-design-pattern.
Bacaan Lanjutan
Referensi Bacaan
https://refactoring.guru/design-patterns/visitor
https://refactoring.guru/design-patterns/visitor/go/example
https://refactoring.guru/design-patterns/visitor-double-dispatch
Author
https://github.com/Mhakimamransyah
https://www.linkedin.com/in/hakim-amr/
mailto: m.hakim.amransyah.hakim@gmail.com
Top comments (0)