Refactoring: Object Orientation Abuser
smell pada Object Orientation Abuser adalah penerapan konsep object oriented yang tidak diterapkan secara penuh. Seperti penggunaan operator switch
yang banyak dan kompleks ataupun penggunaan operator if
yang banyak dan berurut, yang seharusnya dapat digantikan dengan penerapan konsep object oriented. Penggunaan field sementara juga dapat menjadi indikasi bahwa field tersebut sebaiknya dideklarasikan dalam sebuah metode, bukan dalam ruang lingkup kelas.
Jenis-jenis bad code smell pada kategori Object Orientation Abuser adalah:
-
Switch statement
-
Temporary Field
-
Refused Bequest
-
Alternatif classes with different interface
1. OO Abuser: Switch Statement
Switch statement dapat dikategorikan sebagai object orientation abuser apabila penerapannya menjadi terlalu complex. Seringkali sebuah switch menjadi dipecah menjadi beberapa komponen lain pada sebuah program.
Ketika sebuah kondisi ditambahkan, switch code harus dimodifikasi kembali. Beberapa cara yang dapat digunakan untuk mengatasi masalah switch statement adalah:
1.1. Switch Statement: Move Method
Move Method, dapat digunakan ketika sebuah method digunakan lebih sering pada kelas lainnya dari pada kelas itu sendiri. Sehingga perlu dipindahkan metode tersebut pada kelas yang seharusnya. Berikut adalah ilustrasi pemindahan aMethod()
dari Class1
ke Class2
.
1.2. Switch Statement: Replace Conditional with Polymorphism
Ketika terdapat struktur conditional yang menunjukan aksi yang bervariasi sesuai dengan tipe dari object atau properties. Berikut ini adalah contoh dari kelas Burung yang memiliki getKecepatan()
, dimana dalam metode tersebut terdapat kondisi tipe burung yang menentukan perhitungan kecepatan. Kasus seperti ini dapat diterapkan menjadi polymorphism, dengan menerapkan override metode getSpeed()
pada masing-masing subclass dari superclass Burung
. Berikut adalah detail code dari contoh yang dijelaskan diatas:
1.3. Switch Statement: Replace Parameter with Explicit Method
Replace Parameter with Explicit Method adalah teknik refactoring di mana sebuah metode yang memiliki beberapa bagian yang dijalankan tergantung pada nilai suatu parameter diubah dengan memisahkan setiap bagian ke dalam metode-metode terpisah. Setiap metode baru ini menggantikan logika yang sebelumnya diatur oleh parameter dalam metode asli. Teknik ini dilakukan untuk meningkatkan keterbacaan kode dan mengurangi kompleksitas metode yang bergantung pada parameter.
1.4. Switch Statement: Introduce Null Object
Memperkenalkan Null Object. Pada beberapa metode yang mengembalikan nilai, nilai yang dikembalikan ada null
. Hal ini dapat digantikan dengan membuat sebuah object null
yang akan direturn menggantikan nilai null itu sendiri. Berikut ini adalah contoh object null dari kelas Customer
bernama NullCustomer yang dideklarasikan untuk menunjukan nilai null
dari sebuah object.
2. OO Abuser:Temporary Field
Temporary field hanya digunakan untuk menampung sebuah nilai pada kondisi tertentu, sehingga di luar kondisi yang disebutkan nilai dari field tersebut adalah kosong. Penggunaan field sementara juga dapat menjadi indikasi bahwa field tersebut sebaiknya dideklarasikan dalam sebuah metode, bukan dalam ruang lingkup kelas.
Seringkali temporary fields dibuat untuk digunakan pada algoritma yang membutuhkan banyak input. Sehingga dibuat banyak parameter dalam metod tersebut, dimana beberapa field yang dideklarasikan digunakan hanya sebagai penampung nilai sementara. Sehingga ketika algoritma selesai dijalankan, field-field tersebut menjadi tidak berguna.
Cara refactoring yang dapat digunakan untuk memperbaiki kondisi temporary field adalah:
2.1. Ekstrasi kelas
Problem:
Field sementara hanya digunakan dalam kondisi tertentu dan hanya untuk algoritma tertentu.
Solusi:
Pindahkan field sementara dan logikanya ke dalam kelas terpisah.
Contoh Sebelum Refactoring:
class Order {
private String tempField;
void processOrder(String value) {
this.tempField = value;
// Logika algoritma menggunakan tempField
System.out.println("Processing order with tempField: " + tempField);
// Setelah selesai, tempField tidak digunakan lagi
this.tempField = null;
}
}
Contoh Setelah Refactoring:
class OrderProcessor {
private String value;
OrderProcessor(String value) {
this.value = value;
}
void process() {
// Logika algoritma menggunakan value
System.out.println("Processing order with value: " + value);
}
}
class Order {
void processOrder(String value) {
OrderProcessor processor = new OrderProcessor(value);
processor.process();
}
}
2.2. Mengganti metode dengan object method
Problem:
Metode memiliki terlalu banyak parameter atau field sementara.
Solusi:
Pindahkan logika metode ke objek metode yang akan menangani semua parameter dan field sementara.
Contoh Sebelum Refactoring:
class Order {
private String tempField1;
private String tempField2;
void processOrder(String value1, String value2) {
this.tempField1 = value1;
this.tempField2 = value2;
// Logika algoritma menggunakan tempField1 dan tempField2
System.out.println("Processing order with tempField1: " + tempField1 + " and tempField2: " + tempField2);
// Setelah selesai, tempField tidak digunakan lagi
this.tempField1 = null;
this.tempField2 = null;
}
}
Contoh Setelah Refactoring:
class OrderProcessor {
private String value1;
private String value2;
OrderProcessor(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
void process() {
// Logika algoritma menggunakan value1 dan value2
System.out.println("Processing order with value1: " + value1 + " and value2: " + value2);
}
}
class Order {
void processOrder(String value1, String value2) {
OrderProcessor processor = new OrderProcessor(value1, value2);
processor.process();
}
}
2.3. Memperkenalkan null object
Problem:
Field sementara sering diperiksa untuk null
sebelum digunakan.
Solusi:
Gunakan objek null sebagai pengganti null
untuk menghindari pemeriksaan kondisi null
.
Contoh Sebelum Refactoring:
class Order {
private Customer customer;
void processOrder() {
if (customer == null) {
System.out.println("Processing order for guest customer");
} else {
System.out.println("Processing order for customer: " + customer.getName());
}
}
}
Contoh Setelah Refactoring:
class Customer {
String getName() {
return "Regular Customer";
}
}
class NullCustomer extends Customer {
String getName() {
return "Guest Customer";
}
}
class Order {
private Customer customer;
Order(Customer customer) {
this.customer = (customer != null) ? customer : new NullCustomer();
}
void processOrder() {
System.out.println("Processing order for customer: " + customer.getName());
}
}
// Penggunaan
Order orderWithCustomer = new Order(new Customer());
orderWithCustomer.processOrder(); // Output: Processing order for customer: Regular Customer
Order orderWithoutCustomer = new Order(null);
orderWithoutCustomer.processOrder(); // Output: Processing order for customer: Guest Customer
3. OO Abuser:Refused Bequest
Refused Bequest merupakan kondisi yang muncul ketika penerapan inheritance
dimotivasi dari tujuan untuk menggunakan kembali code pada superclass, dimana karakteristik subclass dan superclass sangat berbeda.
Inherintace
merupakan konsep penurunan yang menghubungkan superclass dan subclass melalui karakteristik generalisasi dan spesifikasi. Dimana kelas yang general menjadi superclass dan kelas yang lebih spesifik menjadi subclass. Beberapa hal yang dapat dilakukan untuk mengatasi pelanggaran ini adalah dengan mengganti inheritance dengan delegasi seperti contoh di bawah ini:
4. OO Abuser:Alternatif Classes with Different Interface
Seperti yang ditunjukkan pada ilustrasi dibawah, kondisi yang terjadi pada smell code ini adalah ketika dua kelas atau lebih memiliki metode yang melakukan fungsi yang sama, tapi dengan nama yang berbeda.
Fungsi yang sama tersebut, dapat diekstrasi menjadi sebuah interface yang dapat diimplementasikan kemudian oleh kelas-kelas yang membutuhkan. Berikut ini adalah beberapa alternative yang dapat dilakukan untuk mengekstraksi interface yang dibutuhkan:
4.1. Mengganti nama method
Mengganti nama metode adalah teknik refactoring di mana metode-metode yang melakukan fungsi yang sama tetapi memiliki nama yang berbeda, diganti namanya agar seragam di seluruh kelas yang berbeda. Hal ini dilakukan untuk meningkatkan konsistensi dan keterbacaan kode.
Ketika dua atau lebih kelas memiliki metode yang melakukan fungsi yang sama, tetapi dengan nama yang berbeda, hal ini dapat menyebabkan kebingungan dan kesulitan dalam pemeliharaan kode. Dengan mengganti nama metode tersebut agar seragam, kita dapat membuat kode lebih konsisten dan mudah dipahami. Setelah metode-metode tersebut memiliki nama yang sama, kita bisa mengekstrak fungsi umum ke dalam sebuah interface yang kemudian diimplementasikan oleh kelas-kelas tersebut.
Contoh Sebelum Refactoring:
class ClassA {
void fetchData() {
// Implementasi
}
}
class ClassB {
void retrieveData() {
// Implementasi
}
}
Contoh Setelah Refactoring:
interface DataFetcher {
void fetchData();
}
class ClassA implements DataFetcher {
public void fetchData() {
// Implementasi
}
}
class ClassB implements DataFetcher {
public void fetchData() {
// Implementasi
}
}
4.2. Menambahkan parameter
Menambahkan parameter adalah teknik refactoring di mana parameter tambahan ditambahkan ke metode sehingga satu metode dapat menangani beberapa variasi fungsi yang sebelumnya dipecah menjadi beberapa metode dengan nama yang berbeda.
Dalam beberapa kasus, metode yang memiliki fungsi serupa dapat digabung menjadi satu metode dengan menambahkan parameter tambahan. Dengan cara ini, kita dapat mengurangi duplikasi kode dan menyederhanakan struktur metode. Setelah parameter ditambahkan, metode ini bisa diekstraksi ke dalam interface yang dapat diimplementasikan oleh kelas-kelas terkait.
Contoh Sebelum Refactoring:
class Printer {
void printText(String text) {
// Implementasi
}
void printImage(Image image) {
// Implementasi
}
}
Contoh Setelah Refactoring:
interface Printer {
void print(Object data);
}
class TextPrinter implements Printer {
public void print(Object data) {
String text = (String) data;
// Implementasi
}
}
class ImagePrinter implements Printer {
public void print(Object data) {
Image image = (Image) data;
// Implementasi
}
}
4.3. Parameterized method
Metode parameterisasi adalah teknik refactoring di mana satu metode dibuat lebih fleksibel dengan menggunakan parameter untuk mengatur perilaku atau output dari metode tersebut.
Dengan metode parameterisasi, kita dapat mengurangi jumlah metode yang memiliki fungsi serupa tetapi dengan nama berbeda atau implementasi yang sedikit berbeda. Ini dilakukan dengan menambahkan parameter yang menentukan bagaimana metode tersebut harus berperilaku. Setelah metode telah diparameterisasi, metode ini dapat diekstraksi ke dalam interface yang kemudian diimplementasikan oleh kelas-kelas yang relevan.
Contoh Sebelum Refactoring:
class ReportGenerator {
void generatePDFReport(Data data) {
// Implementasi
}
void generateExcelReport(Data data) {
// Implementasi
}
}
Contoh Setelah Refactoring:
interface ReportGenerator {
void generateReport(Data data, String format);
}
class PDFReportGenerator implements ReportGenerator {
public void generateReport(Data data, String format) {
if (format.equals("PDF")) {
// Implementasi PDF
}
}
}
class ExcelReportGenerator implements ReportGenerator {
public void generateReport(Data data, String format) {
if (format.equals("Excel")) {
// Implementasi Excel
}
}
}
Kesimpulan
-
Object Orientation Abuser adalah penerapan konsep object oriented yang tidak diterapkan secara penuh.
-
Jenis-jenis smell code pada kategori Object Orientation Abuser adalah switch statement, temporaty field, refused bequest dan alternative class with different interface.
Referensi
-
Steve Halladay. (2012). Principle-Based Refactoring. 01. Principle Publishing. Indianapolis. ISBN: 978-0615690223.
-
Bad Code Smells, https://sourcemaking.com/refactoring/smells
-
M. Fowler and K. Beck, "Bad Smells in Code," in Refactoring: Improving the Design of Existing Code, Addison-Wesley, 2000
-
OO Abusers,