Object Creation di Object Oriented Programming

Aditya Novandri
5 min readMay 2, 2024

Di artikel sebelumnya dijelaskan secara umum apa itu Object Oriented Programming dimana dalam membuat sebuah program kita memodelkannya menjadi object-object yang saling berinteraksi. Object tersebut adalah instance dari class yang dideklarasikan.

Di pemrograman menggunakan bahasa Java kita seringkali menggunakan keyword “new” yang disertai dengan memanggil public constructor dari sebuah class untuk membuat sebuah object baru.

//Membuat instance object product dari class Product
Product product = new Product();

Ada beberapa teknik lain yang bisa digunakan untuk membuat sebuah object antara lain dengan menggunakan static factory. Sebuah class dapat menyediakan sebuah public method yang merupakan static factory (method yang bersifat static yang langsung mengembalikan instance dari class tersebut atau dari class yang lain).

Static factory disini berbeda dengan Factory Pattern yang merupakan salah satu Creational Design Pattern (detail akan kita bahas di artikel yang lain).

Sebagai contoh adalah implementasi dari class java.lang.Boolean.

public final class Boolean implements Serializable, Comparable<Boolean>, Constable {
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
...
...
public static Boolean valueOf(boolean b) {
return b ? TRUE : FALSE;
}
}

Dengan memanggil Boolean.valueOf(?) kita bisa langsung mendapatkan sebuah object yang berupa Boolean(true) atau Boolean(false) tergantung dari parameter apa yang dikirimkan.

Ada beberapa kelebihan ketika kita menggunakan static factory dalam membuat sebuah object.

1. Static factory memiliki nama method. Tidak seperti constructor, kita bisa memberikan nama method untuk sebuah implementasi static factory. Sebagai contoh adalah implementasi dari class java.time.Duration.

public final class Duration
implements TemporalAmount, Comparable<Duration>, Serializable {
...
private Duration(long seconds, int nanos) {
super();
this.seconds = seconds;
this.nanos = nanos;
}
...
public static Duration ofHours(long hours) {
return create(Math.multiplyExact(hours, SECONDS_PER_HOUR), 0);
}
...
public static Duration ofSeconds(long seconds) {
return create(seconds, 0);
}
...
private static Duration create(long seconds, int nanoAdjustment) {
if ((seconds | nanoAdjustment) == 0) {
return ZERO;
}
return new Duration(seconds, nanoAdjustment);
}
}

Class Duration diatas mempunyai static factory method antara lain : ofHours(long hours) dan ofSeconds(long seconds). Akan lebih mudah bagi client untuk menentukan apakah akan membuat sebuah object Duration dalam hours atau seconds dengan memanggil method yang memiliki “nama” yang mendefinisikan fungsi dari method tersebut.

Kita juga bisa memberikan logic tertentu didalam static factory method sebelum membuat object nya. Sebagai contoh di method ofHours(long hours), dimana parameter hours akan dikonversi terlebih dahulu ke seconds sebelum membuat sebuah object baru.

Math.multiplyExact(hours, SECONDS_PER_HOUR)

2. Static factory tidak selalu membuat object instance yang baru. Ketika kita menggunakan keyword new maka object yang baru akan selalu dibuat. Dengan menggunakan static factory kita bisa melakukan kontrol apakah akan selalu membuat object yang baru atau mengembalikan object yang sebenarnya sudah dibuat sebelumnya (Singleton Pattern). Sebagai contoh adalah implementasi dari class org.slf4j.impl.StaticLoggerBinder.

public class StaticLoggerBinder implements LoggerFactoryBinder {
...
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
...

private StaticLoggerBinder() {
this.defaultLoggerContext.setName("default");
}

public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}

...
static void reset() {
SINGLETON = new StaticLoggerBinder();
SINGLETON.init();
}
...
}

Static factory StaticLoggerBinder.getSingleton ini akan mengembalikan object SINGLETON yang merupakan implementasi dari “new StaticLoggerBinder()” dan sudah dibuat sebelumnya. Constructor dari class StaticLoggerBinder ini dibuat private yang mencegah client untuk membuat object secara langsung tanpa melalui implementasi static factory nya. Kelebihan dari Singleton ini adalah kita bisa mengurangi cost atas kebutuhan untuk membuat object yang baru ketika memang tidak dibutuhkan.

3. Static factory memungkinkan untuk mengembalikan tipe object yang merupakan sub tipe dari tipe object yang lain. Menyembunyikan detail dan mengembalikan implementasi dari sebuah interface. Tidak seperti ketika membuat object dengan menggunakan keyword new dimana setiap kali kita membuat object akan selalu membuat object dengan tipe yang sama sesuai dengan class nya.

Sebagai contoh ada di class java.util.Collections. Class Collections mempunyai kurang lebih 40 class implementasi dari interface java.util.Collection dan salah satunya adalah EmptySet.

package java.util

public class Collections {
private Collections() {}

...
public static final Set EMPTY_SET = new EmptySet<>();

public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}

private static class EmptySet<E>
extends AbstractSet<E>
implements Serializable {
...
...
...
}
}

EmptySet sendiri merupakan sub tipe dari AbstractSet, dimana static factory method yang digunakan untuk membuat sebuah empty set dideklarasikan di class Collections

Kita bisa memanggil implementasi class diatas dengan menggunakan code seperti ini

Set<TreeSet> treeSet = Collections.emptySet();

Snippet code diatas berfungsi untuk membuat sebuat object Set (yang merupakan sub tipe dari interface Collection) dimana implementasi nya adalah sebuah TreeSet.

4. Static factory dapat mengembalikan object yang merupakan implementasi dari class yang dipanggilnya (Factory Pattern). Sebagai contoh dapat ditemukan di implementasi class java.util.EnumSet

package java.util;

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
...

public static <E extends Enum<E>> EnumSet<E> of(E e) {
EnumSet<E> result = noneOf(e.getDeclaringClass());
result.add(e);
return result;
}
...
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");

if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
...
}

Method noneOf diatas berfungsi sebagai factory dimana ketika length dari variabel Array universe kurang dari atau sama dengan 64 maka static factory ini akan mengembalikan object RegularEnumSet, dan jika lebih dari 64 maka akan mengembalikan object JumboEnumSet.

Baik class RegularEnumSet maupun JumboEnumSet merupakan sub tipe dari EnumSet itu sendiri

package java.util;

class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
...
}

class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
...
}

Keuntungannya adalah detail implementasi dari RegularEnumSet dan JumboEnumSet disembunyikan dari client, yang berarti ketika ada perubahan logic didalam RegularEnumSet dan JumboEnumSet akan transparan disisi client.

Ada beberapa nama method yang umum digunakan untuk static factory, antara lain :

  • from : Digunakan untuk mengkonversi satu parameter dengan tipe data tertentu menjadi sebuah object implementasi dari class dimana static factory tersebut berada.
Date d = Date.from(instant);
  • of : Berfungsi sebagai method aggregation. Menyediakan beberapa parameters dan mengembalikan object implementasi yang sesuai dengan tipe nya.
Set<Product> products = EnumSet.of(ELECTRONIC, FASHION, BOOK);
  • valueOf : Sebagai alternatif dari from dan of
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance atau getInstance : Digunakan untuk mengembalikan object yang sudah diimplementasikan sebelumnya sesuai dengan parameter yang dikirimkan (jika ada); bisa berarti Singleton Pattern.
StackWalker luke = StackWalker.getInstance(options);
  • create atau newInstance : Hampir sama dengan instance atau getInstance, hanya saja setiap kali dipanggil object yang dikembalikan adalah object yang baru.
Object newArray = Array.newInstance(classObject, arrayLen);
  • getType : Sama seperti pada getInstance, hanya saja static factory method nya ada di class yang lain dan berbeda dengan tipe object yang dikembalikan.
FileStore fs = Files.getFileStore(path);
  • newType : Sama seperti pada newInstance, hanya saja static factory method nya ada di class yang lain dan berbeda dengan tipe object yang dikembalikan.
BufferedReader br = Files.newBufferedReader(path);
  • type : Alternatif lain dari getType dan newType.
List<Product> products = Collections.list(allProducts);

Selain itu masih ada cara lain yang lazim digunakan untuk membuat sebuah object di Object Oriented Programming antara lain penggunaan Builder Pattern dan Dependency Injection (akan dibahas di artikel selanjutnya)

Referensi diambil dari buku Effective Java (Joshua Bloch)

--

--