• Cum. May 20th, 2022

Merhabalar arkadaşlar.

Bu yazıda sizlere Java dili ile Builder Design Pattern nasıl uygulanır onu anlatmaya çalışacağım.

Geliştirilen yazılımlarda POJO olarak adlandırabilecek sınıflarımızı kullanıyoruz ve bu sınıflardan nesne elde ederken constructor’lardan faydalanabiliyoruz. Gelgelelim ki POJO içerisindeki alanların sayısı arttıkça constructor kullanımı zora girmekte ve kodlama açısında hoş olmayan bir durum ortaya çıkmaktadır.

Ne demek istediğimi kod üzerinde göstereyim. User sınıfımız olduğunu ve şöyle bir constructor’ımız olduğunu düşünelim:

public User(String firstName, String surname, int age, String phone, String address) {
        this.firstName = firstName;
        this.surname = surname;
        this.age = age;
        this.phone = phone;
        this.address = address;
    }

Bu yapılandırıcıya baktığımızda iki şey görüyoruz. Birincisi bir User nesnesi oluşturulacağı zaman yapılandırıcıya beklediği sayı kadar parametre geçirilmesi gerekiyor. Mesela ben User nesnesi oluşturulurken phone ya da address bilgilerinin zorunlu olarak geçirilmesini istemezsem ne olacak? İlk akla gelen bir constructor daha yazarım ve orada da phone hariç diğer 4 parametreyi alırım olabilir ancak bu da doğru bir yaklaşım değil. Daha esnek ve kod bakımından düzgün bir yol bulmamız gerek.

İkinci ise User nesnesi oluşturulurken ortaya çıkacak, uzun ve çirkin görünecek constructor kod yapısı.

Builder design pattern bu iki sorunu çözmek için bir yöntem sunuyor. Şimdi sunduğu bu yöntemi inceleyelim.

Elimizdeki User POJO’sunun yeni hali şöyle olacaktır:

package com.ilkaygunel.builderdesignpattern.pojo;

public class User {

    private final String firstName;
    private final String surname;
    private final int age;
    private final String phone;
    private final String address;

    public User(UserBuilder userBuilder) {
        this.firstName = userBuilder.firstName;
        this.surname = userBuilder.surname;
        this.age = userBuilder.age;
        this.phone = userBuilder.phone;
        this.address = userBuilder.address;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSurname() {
        return surname;
    }

    public int getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return "User: " + this.firstName + ", " + this.surname + ", " + this.age + ", " + this.phone + ", " + this.address;
    }

    public static class UserBuilder {

        private final String firstName;
        private final String surname;
        private int age;
        private String phone;
        private String address;

        public UserBuilder(String firstName, String surname) {
            this.firstName = firstName;
            this.surname = surname;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

        public UserBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }

        public User build() {
            User user = new User(this);
            return user;
        }
    }
}

User sınıfını incelediğimizde şunlarla karşılaşacağız:

  • Sınıf içerisindeki alanların tümü final olarak tanımlanmış. Java’da eğer bir değişken final olarak tanımlanırsa Java size bu değişkene kod içerisinde bir değer atanmasını zorunlu kılar ve bu değeri bir daha değiştiremezsiniz. Bu durum işimize yarar bir durumdur çünkü bir User nesnesi oluştuğunda değiştirilmemesini istediğiniz, şart koştuğunuz alanlar olabilir. Ayrıca bu final tanımlanması sınıf içerisindeki alanları zorunlu tutmuştur fakat bu zorunluluk User nesnesi oluşturulmasından gelmemektedir. Oluşturulan her User nesnesi değer atansa da atanmasa da bu alanlara sahip olacak demektir.
  • User constructor’ına baktığımız zaman bir UserBuilder nesnesini parametre aldığını görüyoruz. Az sonra main metodu’nu incelerken bu constructor’ın kullanımını göreceğiz.
  • Sınıf içerisindeki değişkenlerin sadece get metotları var, çünkü User sınıfı üzerinden bir set işlemi olmayacak.
  • Inner Class olarak tabir edilen bir UserBuilder sınıfını User sınıfı içinde yazdık. UserBuilder içerisinde sadece firstName ve surname alanları final olarak tanımlı çünkü User nesnesinin en azından bu iki alana sahip olmasını istiyoruz ve final tanımlayarak da ilk değer atamasını zorunlu kılıyoruz. Bu ilk değer ataması da UserBuilder constructor’ı ile olacak.
  • Diğer alanlar için ise set metotlarına benzer bir metot yazdık. Farklı olarak bu metotlar UserBuilder tipinde dönüş yapıyorlar. Bu dönüşün amacını da az sonra main metodu içinde göreceğiz.

Şimdi de main metodunu inceleyelim:

package com.ilkaygunel.builderdesignpattern.main;

import com.ilkaygunel.builderdesignpattern.pojo.User;

public class MainClass {

    public static void main(String[] args) {
        User eyyup = new User.UserBuilder("Eyyüp", "Bağdaş")
                .age(26)
                .phone("+90 216 504 5655")
                .address("Akasya Acıbadem Ofis Kuleleri\n A Blok 24. Kat No:127\n Acıbadem İstanbul Turkey")
                .build();

        User kevser = new User.UserBuilder("Kevser", "Köse")
                .age(22)
                .phone("+90 216 504 5655")
                .build();

        User merve = new User.UserBuilder("Merve", "Topal")
                .age(22)
                .build();

        System.out.println(eyyup);
        System.out.println(kevser);
        System.out.println(merve);
    }
}

Main metodumuz içerisinde 3 tane User nesnesi oluşturuyoruz. Nesne oluşturulması sırasında biz User içerisinde static class olarak tanımlanmış UserBuilder’a User.UserBuilder diyerek direk erişebiliyoruz. UserBuilder’ın yapılandırıcısına istediği bilgileri verdikten sonra doldurmak istediğimiz diğer alanları dolduruyoruz. Az önce UserBuilder içinde set metoduna benzeyen metotlardan bahsetmiştik ve bu metotların UserBuilder tipinde dönüş yaptığını söylemiştik. Bu dönüşün amacı peş peşe bir şekilde UserBuilder içerisindeki metotlara erişim amacıdır. Örneğin age metodu UserBuilder tipinde dönüş yaparak bizim UserBuilder içindeki diğer metotlara hemen arkasından erişmemizi sağlıyor.

eyyup nesnesi için baktığımızda age, phone ve address bilgilerini dolduruyoruz. Son olarak da build deyip bir User nesnesi elde edip bunu eyyup nesnesine atıyoruz.

kevser nesnesi için baktığımızda ise address bilgisinin atanmadan bir User nesnesinin elde edilebilidiğini görüyoruz. merve nesnesinde isim, soyisim bilgisine ek olarak sadece yaş bilgisi var.

Main metodunu çalıştırdığımızda konsol çıktısı şöyle olmakta:

User nesnelerini bu şekilde alanların bir kısmı zorunlu bir kısmı isteğe bağlı dolduralacak şekilde oluşturabiliriz.

Bu yazıda anlatacaklarım bu kadar arkadaşlar. Başka bir yazıda görüşene kadar sağlıcakla kalın.

Görüşmek üzere.

Selam ve Sevgilerimle

Not: Bu yazıda http://howtodoinjava.com/design-patterns/creational/builder-pattern-in-java/ adresinden faydalanılmıştır.

Merhaba! Ben İlkay Günel :) İstanbul Üniversitesi Bilgisayar Mühendisliği 2016 mezunuyum. Güncel olarak Paycore'da SoftPOS projesinde Java Developer olarak çalışmaktayım. Büyük oranda Java teknolojileri ile uğraşıp kendimi geliştirmeye çalışıyorum ve bu mecrada öğrendiklerimi sizle paylaşmaya da çabalıyorum :) Hakkımda biraz daha detaylı bilgi için: http://www.ilkaygunel.com/about/

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir