Tasarım Kalıpları: Prototype (Prototip ya da Örnek Nesne)
GoF’un nesne yaratmayla ilgili 5 kalıbından bir diğeri de Prototype’dır. Dilimize “prototip” ya da “örnek nesne” olarak çevrilebilecek olan bu kalıbın amacıyla ilgili GoF’da şu bilgiler bulunmaktadır:
Intent: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying prototype.
Amaç: Prototip bir nesneyi kullanarak yaratılacak nesneleri belirlemek ve yeni nesneleri prototipi kopyalayarak oluşturmak.
Anlaşılması son derece kolay ve tabi olan bu kalıbı özellikle karmaşık ve durumları arasında az farklar olan nesneleri yaratmada başarıyla kullanabiliriz.
Uygulamalarda sıklıkla iş sınıflarından nesneler oluştururuz. Bu nesneler zaman zaman ciddi karmaşıklıkta olurlar. Aynı tipten olan nesneleri sıklıkla farklı durumlarla yaratmak isteriz: UnAuthenticatedUser ve AuthenticatedUser gibi.
Bazen karmaşık bir nesneyi yaratıp, o nesneyi kopyalayarak yeni nesneler yaratabiliriz. Kopyalanan nesne prototip nesnedir. Bazen de karmaşık bir nesneyi, farklı durumlarla yaratmak isteriz. Yani nesnenin ilk yaratıldığında alacağı farklı durumlar ise birbirlerinden türetilebilir.
Çözüm olarak diğer nesnelerin yaratılabilmesi için oluşturulan örnek nesne ya da prototipten başlayabiliriz. Yani prototip olarak oluşturulan bir nesne, aynı türden diğer nesnelerin yaratılması için bir başlangıç noktasıdır. Dolayısıyla, yeni nesneler oluşturmak, için var olan prototipi kopyalarız (cloning). Muhtemelen prototype ile diğer nesneler arasında durum farklılıkları vardır. İstenirse bu farklı durumlar tek bir prototipten üretilen nesne üzerinde, gerekli setter metotları çağrılarak oluşturulur. İstenirse de farklı durumlar için farklı prototipler üretilir.
Örneğin aşağıdaki gibi örnek amacıyla yapılmış olduğundan basit yapıyı ele alalım.
Örnek kodlar da aşağıdaki gibi olacaktır.
package org.javaturk.dp.pattern.gof.creational.prototype.simple; public abstract class Customer implements Cloneable { protected int id; protected String address; protected String phone; boolean authenticated; public Customer(int id, String address, String phone) { this.id = id; this.address = address; this.phone = phone; authenticated = false; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public boolean isAuthenticated() { return authenticated; } public void setAuthenticated(boolean authenticated) { this.authenticated = authenticated; } @Override public String toString() { return "Customer [id=" + id + ", address=" + address + ", phone=" + phone + ", authenticated=" + authenticated + "]"; } @Override public Customer clone() { Customer customer = null; try { customer = (Customer) super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Problem when cloning the object: " + e.getMessage()); e.printStackTrace(); } return customer; } }
Görüldüğü gibi nesnenin “clone()” metodu override edilmiştir.
Benzer şekilde yapıdaki diğer iki nesne de şöyledir:
package org.javaturk.dp.pattern.gof.creational.prototype.simple; import java.util.Date; public class IndividualCustomer extends Customer{ private String firstName; private String lastName; private Date dob; public IndividualCustomer(int id, String address, String phone, String firstName, String lastName, Date dob) { super(id, address, phone); this.firstName = firstName; this.lastName = lastName; this.dob = dob; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Date getDob() { return dob; } public void setDob(Date dob) { this.dob = dob; } @Override public String toString() { return "IndividualCustomer [firstName=" + firstName + ", lastName=" + lastName + ", dob=" + dob + ", id=" + id + ", address=" + address + ", phone=" + phone + ", authenticated=" + authenticated + "]"; } }
package org.javaturk.dp.pattern.gof.creational.prototype.simple; public class CorporateCustomer extends Customer { private String corporateName; private double discount; public CorporateCustomer(int id, String address, String phone, String corporateName, double discount) { super(id, address, phone); this.corporateName = corporateName; this.discount = discount; } public String getCorporateName() { return corporateName; } public void setCorporateName(String corporateName) { this.corporateName = corporateName; } public double getDiscount() { return discount; } public void setDiscount(double discount) { this.discount = discount; } @Override public String toString() { return "CorporateCustomer [corporateName=" + corporateName + ", discount=" + discount + ", id=" + id + ", address=" + address + ", phone=" + phone + ", authenticated=" + authenticated + "]"; } } Bu durumda client kodumuz da basitçe şöyle olacaktır:
package org.javaturk.dp.pattern.gof.creational.prototype.simple; import java.util.Date; public class Test { public static void main(String[] args) { IndividualCustomer unAuthenticatedCoskunCustomerPrototype = new IndividualCustomer(1, "Cumhuriyet Cad. No 28 Beşiktaş Istanbul", "2128399041", "Selim", "Coskun", new Date()); System.out.println(unAuthenticatedCoskunCustomerPrototype); IndividualCustomer authenticatedAhmetCoskun = (IndividualCustomer) unAuthenticatedCoskunCustomerPrototype.clone(); authenticatedAhmetCoskun.setId(2); authenticatedAhmetCoskun.setFirstName("Ahmet"); authenticatedAhmetCoskun.setAuthenticated(true); System.out.println(authenticatedAhmetCoskun); CorporateCustomer unAuthenticatedGECustomerPrototype = new CorporateCustomer(100, "Manolya Cad. No 5 Sariyer Istanbul", "2124199047", "GE", 0.25); System.out.println(unAuthenticatedGECustomerPrototype); } }
Bu basit uygulamadan da görüldüğü gibi, karmaşık nesneler, bir örnek nesneden clonelanarak oluşturulup, gerekirse olması gereken duruma getirilebilir. Okuyuculara ödev olarak aşağıdaki durumu verebiliriz:
public JTUser execute(RegistrationForm form, HttpServletRequest request, Locale locale) throws JTException { String uid = utils.generateUUID(); String salt = utils.generateSalt(32); String accountId = utils.generateUUID(); JTUser user = new JTUser( form.getEmail().trim(), null, true, true, true, true, AuthorityUtils.NO_AUTHORITIES, // spring security fields uid, formorm.getEmail().trim(), salt, form.getName().trim(), form.getSurname().trim(), null, accountId, true, true, form.getWelcomeMessage().trim(), true, new Date(), null); user.setStatus(JTConstants.PENDING); String servletPath = request.getServletPath(); if (executeInner(form, locale, user)) { auditLogDao.insert(new AuditLog(user.getUid(), user.getUsername(), utils.getRemoteIp(), utils.getSid(), AuditType.REGISTRATION, AuditSubtype.PENDING, ResponseCode.SUCCESS, new Date(), null)); authenticateFastRegisterUser(form, request); } return user;
Yukarıdaki kodu prototype kalıbıyla nasıl iyileştirebiliriz? Kodda JTUser oluşturulurken pek çok boolean parametre geçildiği görülüyor. Muhtemelen sistemde farklı JTUser nesneleri farklı boolean değerlerle oluşturuluyor. Bu durumda prototype kalıbı kullanışlı görünüyor.
Benzer durum AuditLog nesnesi için de geçerli midir?
Prototype kalıbının artısı, Factory ya da Abstract Factory kalıplarında var olan miras mekanizmasını kullanmamasıdır. Çünkü yeni nesneler sadece var olan prototipten üretilirler, ama factorylerde her nesne için ayrı bir üretici sınıf vardır. Bu yüzden derin factory hiyerarşisinden kaçmak için Prototype kalıbını kullanabilirsiniz.
Factory Method ve Abstract Factory nesne oluştururken Prototype kalıbını kullanabilir. Prototype, Composite ve Decorator kalıplarında sıklıkla kullanılır.
Toplam görüntülenme sayısı: 2769