Monday, October 5, 2009

Design Pattern in Brief (5)

Flyweight

Setiap object mengandung informasi yang tersimpan dalam propertynya. Informasi tersebut bisa dibagi menjadi dua kategori yaitu informasi intrinsik dan ekstrinsik. Informasi intrinsik adalah informasi yang independen, yaitu informasi yang bersifat tetap atau konstan terhadap instance suatu class. Contohnya adalah informasi nama dan alamat perusahaan pada class kartu identitas pegawai. Informasi ekstrinsik adalah informasi yang bersifat berubah-ubah tergantung object tempat informasi tersebut berada. Hal ini berarti informasi tersebut "unique" untuk tiap instance dari class. Sebagai contoh, nama dan jabatan pegawai adalah "unique" untuk tiap pegawai.

Sekarang misalkan kita memiliki sebuah class employee yang bisa dibuat menjadi ribuan object dan di dalam class tersebut tersimpan informasi intrinsik dan ekstrinsik. Jika hal itu terjadi maka akan menimbulkan overhead pada memori karena menyimpan tiap informasi intrinsik yang sebetulnya sama bagi semua object employee tersebut. Untuk mengatasi hal inilah pattern flyweight dibuat.

Pattern flyweight menyarankan untuk memisah informasi intrinsik ke dalam object sendiri yang disebut object flyweight. Tiap object yang membutuhkan object flyweight dapat berbagi instance flyweight sehingga akan mampu menghemat memori dan waktu yang dibutuhkan untuk membuat object (object creation time).

Berikut adalah requirement yang harus dipenuhi untuk membuat flyweight :
- Hanya ada satu object flyweight dan object tersebut digunakan bersama oleh object lain yang membutuhkan.
- Object yang memanfaatkan flyweight di atas tidak boleh secara langsung membuat object flyweight.

Untuk memenuhi requirement di atas maka berikut adalah pendekatan yang dilakukan
- Class flyweight harus memiliki konstruktor private sehingga tidak bisa dibuat oleh class lain.
- Pattern singleton digunakan untuk memastikan bahwa tidak ada duplikasi object.

package edu.mat.pattern;

public interface IFlyweight {

public String getCompanyName();
public String getCompanyAddress();

}

package edu.mat.pattern;

import java.util.HashMap;
import java.util.Map;

public class FlyweightFactory {

private static final FlyweightFactory INSTANCE = new FlyweightFactory();
private Map flyweightList;

private FlyweightFactory() {
super();
this.flyweightList = new HashMap();
}

public static FlyweightFactory getInstance() {
return INSTANCE;
}

public synchronized Flyweight getFlyweight(String companyName) {
if(flyweightList.get(companyName) == null) {
Flyweight fly = new Flyweight(companyName);
flyweightList.put(companyName, fly);
return fly;
}
else
return flyweightList.get(companyName);
}

private class Flyweight implements IFlyweight {

private String companyName;
private String companyAddress;

private Flyweight(String companyName) {
super();
this.companyName = companyName;
if(companyName != null) {
if(companyName.equals("IBM"))
this.companyAddress = "California";
else if(companyName.equals("SUN"))
this.companyAddress = "Silicon Valley";
}
}

@Override
public String getCompanyAddress() {
return companyAddress;
}

@Override
public String getCompanyName() {
return companyName;
}

}

}

package edu.mat.pattern;

public class Main {

public static void main(String[] args) {
FlyweightFactory factory = FlyweightFactory.getInstance();
System.out.println(factory.getFlyweight("SUN"));
System.out.println(factory.getFlyweight("IBM"));
System.out.println(factory.getFlyweight("SUN"));
System.out.println(factory.getFlyweight("IBM"));
}
}

Saat dijalankan akan menghasilkan :

edu.mat.pattern.FlyweightFactory$Flyweight@42e816
edu.mat.pattern.FlyweightFactory$Flyweight@9304b1
edu.mat.pattern.FlyweightFactory$Flyweight@42e816
edu.mat.pattern.FlyweightFactory$Flyweight@9304b1

Perhatikan bahwa tepat hanya ada satu object flyweight untuk tiap companyName yang sama.

Sunday, October 4, 2009

Design Pattern in Brief (4)

Iterator

Iterator digunakan untuk mengakses isi sebuah container secara urut tanpa harus mengetahui isi container tersebut. Container yang dimaksud adalah collection of data. Di java, Iterator dapat diterapkan dengan implement java.util.Iterator. Ada dua macam iterator yaitu internal dan eksternal.

Misalkan kita memiliki sebuah container, maka internal iterator adalah iterator yang dimaintain oleh container tersebut. Container bertanggung jawab terhadap state dari iterator dan hanya bisa ada satu iterator dalam satu container. Sedangkan eksternal iterator adalah iterator yang diletakkan dalam object terpisah dari container. Jadi sekarang ada dua object, container dan iterator, di mana container mengembalikan sebuah object bertipe iterator dalam sebuah methodnya, misalnya getIterator().

Berikut adalah contoh internal iterator :
package edu.mat.pattern.iterator;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Vector;

/**
* Internal Iterator means the collection itself needs to maintain the iterator,
* that's why this collection extends Iterator. So this class can be viewed as both
* a container and an iterator.
* */
public class InternalIterator implements Iterator {

private Vector data;
private Enumeration enumeration;
private Employee nextEmployee;

public InternalIterator() {
data = new Vector();
data.add(new Employee("Santoso", "Los Angeles"));
data.add(new Employee("Rochmat", "New York"));

enumeration = data.elements();
}

@Override
public boolean hasNext() {
nextEmployee = null;
while(enumeration.hasMoreElements()) {
Employee employee = enumeration.nextElement();
nextEmployee = employee;
if(employee != null)
return true;
}
return false;
}

@Override
public Employee next() {
if(nextEmployee == null)
throw new NoSuchElementException();
else
return nextEmployee;
}

@Override
public void remove() {
// do nothing because we don't wanna remove anything

}

}

Dan berikut adalah contoh eksternal iterator yang terdiri dari container dan iterator :
package edu.mat.pattern.iterator;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

public class ExternalContainer {

private Vector data;

public ExternalContainer() {
data = new Vector();
data.add(new Employee("Santoso", "Los Angeles"));
data.add(new Employee("Rochmat", "New York"));
}

public Enumeration getAllEmployees() {
return data.elements();
}

public Iterator getEmployeeByName(String name) {
return new ExternalIterator(this, name);
}

}

package edu.mat.pattern.iterator;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class ExternalIterator implements Iterator {

private String name;
private Enumeration enumeration;
private Employee nextEmployee;

public ExternalIterator(ExternalContainer employees, String name) {
this.name = name;
this.enumeration = employees.getAllEmployees();
}

@Override
public boolean hasNext() {
nextEmployee = null;
while(enumeration.hasMoreElements()) {
Employee employee = enumeration.nextElement();
nextEmployee = employee;
if(employee != null) {
if(name == null)
return true; // jika tidak ada filter maka return true
else {
if(employee.getName() != null) {
if(name.equals(employee.getName()))
return true; // jika ada filter dan curr employee name sama maka return true
// jika ada filter tapi curr employee name tdk sama maka continue
} // jika ada filter tapi curr employee name null maka continue
}
}
}
return false; // jika next employee = null maka return false
}

@Override
public Employee next() {
if(nextEmployee == null)
throw new NoSuchElementException();
else
return nextEmployee;
}

@Override
public void remove() {
// do nothing because we don't wanna remove anything

}

}

Thursday, September 17, 2009

Design Pattern in Brief (3)

Composite

Setiap komponen atau objek bisa dikelompokkan ke dalam salah satu dari dua kategori, individual komponen atau komposit komponen (yang terdiri dari individual komponen dan komposit komponen lain). Composite pattern digunakan untuk memodelkan sebuah interface sehingga kedua komponen tersebut bisa dilihat sebagai sebuah komponen yang sama dari sisi klien. Sederhananya, klien akan melihat dan bisa memperlakukan kedua komponen tersebut dengan cara yang sama.

Implementasi composite bisa dilihat pada tree, yang terdiri dari parent nodes dan leaves. Berikut ini kita mempunyai sebuah interface FileSystemComponent yang mempunyai method-method untuk megakses file komponen dan direktori komponen.



Method addComponent() pada class DirComponent digunakan untuk menambah file atau direktori baru di susunan file sistem tersebut. Sedangkan untuk mendapatkan ukuran dari file sistem yang ada, klien cukup memanggil getComponentSize(). Klien memperlakukan sama kedua file sistem tersebut, file dan direktori, untuk mendapatkan ukuran file sistem yang ada.

Design berikut adalah perbaikan dari design sebelumnya.



Kita bisa memanfaatkan interface atau abstract class dengan memindahkan semua method di DirComponent ke interface atau abstract tersebut. Karena class FileComponent tidak seharusnya memiliki method selain getComponentSize() maka FileComponent bisa throw exception jika method2 tersebut dipanggil dari dirinya. Sedangkan DirComponent harus mengoverride method2 tersebut.

public abstract class FileSystemComponent {
String name;
public FileSystemComponent(String cName) {
name = cName;
}
public void addComponent(FileSystemComponent component)
throws CompositeException {
throw new CompositeException(
"Invalid Operation. Not Supported");
}
public FileSystemComponent getComponent(int componentNum)
throws CompositeException {
throw new CompositeException(
"Invalid Operation. Not Supported");
}
public abstract long getComponentSize();
}

public class FileComponent extends FileSystemComponent {
private long size;
public FileComponent(String cName, long sz) {
super(cName);
size = sz;
}
public long getComponentSize() {
return size;
}
}

public class DirComponent extends FileSystemComponent {
Vector dirContents = new Vector();
//individual files/sub folders collection
public DirComponent(String cName) {
super(cName);
}
public void addComponent(FileSystemComponent fc)
throws CompositeException {
dirContents.add(fc);
}
public FileSystemComponent getComponent(int location)
throws CompositeException {
return (FileSystemComponent) dirContents.elementAt(
location);
}
public long getComponentSize() {
long sizeOfAllFiles = 0;
Enumeration e = dirContents.elements();
while (e.hasMoreElements()) {
FileSystemComponent component =
(FileSystemComponent) e.nextElement();
sizeOfAllFiles = sizeOfAllFiles +
(component.getComponentSize());
}
return sizeOfAllFiles;
}
}

Yang perlu diperhatikan adalah bahwa setiap kali kita menambah method baru, misal removeComponent() untuk DirComponent, maka kita juga harus menambahkannya di abstract atau interfacenya (dan FileComponent harus mengoverridenya). Kenapa? Karena kedua class child, FileComponent dan DirComponent, harus bisa dilihat dan diperlakukan sama oleh klien.

Saturday, September 12, 2009

Debugging in GWT

Days a go I tried to debug my GWT code and... nothing happened. In GWT, we can debug our code in hosted mode but not in web mode by the way. My breakpoint didn't hit by debugger. How could it be? After googling for awhile I found out that I'm not the only one who has the same problem. Some people say that they have problem when it is jdk 1.6.0_14, and when they downgrade to 1.6.0_13, it runs well without any debugging problems.

But before I revert back to older version, it's a good practice to upgrade first. So I decided to upgrade to 1.6.0.16 (currently available version). And when I run eclipse debugger, it works. Fiuh...

Design Pattern in Brief (2)

Prototype

Prototype bisa dipandang sebagai salah satu alternatif dari Factory dan Abstract Factory. Untuk memahaminya bisa dilihat pada contoh-contoh berikut.



Design di atas bisa diimplementasikan dengan abstract factory seperti berikut :



Dengan prototype kita bisa mengimplementasikannya seperti :





public class HostingPlanKit {
private HostingPlan basicPlan;
private HostingPlan premiumPlan;
private HostingPlan premPlusPlan;
public HostingPlanKit(HostingPlan basic, HostingPlan premium,
HostingPlan premPlus) {
basicPlan = basic;
premiumPlan = premium;
premPlusPlan = premPlus;
}
public HostingPlan getBasicPlan() {
return (HostingPlan) basicPlan.clone();
}
public HostingPlan getPremiumPlan() {
return (HostingPlan) premiumPlan.clone();
}
public HostingPlan getPremPlusPlan() {
return (HostingPlan) premPlusPlan.clone();
}


public class HostingPlanManager {
public static HostingPlanKit getHostingPlanKit(
String platform) {
HostingPlan basicPlan = null;
HostingPlan premiumPlan = null;
HostingPlan premPlusPlan = null;
if (platform.equalsIgnoreCase("Win")) {
basicPlan = new WinBasic();
premiumPlan = new WinPremium();
premPlusPlan = new WinPremPlus();
}
if (platform.equalsIgnoreCase("Unix")) {
basicPlan = new UnixBasic();
premiumPlan = new UnixPremium();
premPlusPlan = new UnixPremPlus();
}
return new HostingPlanKit(basicPlan, premiumPlan,
premPlusPlan);
}
}

Thursday, September 10, 2009

Shallow Copy & Deep Copy

Semua java class memilik method clone() yang diturunkan dari java.lang.Object. Method tersebut membuat copy dari sebuah object sebagai shallow copy. Apa yang dimaksud dengan shallow copy adalah :
1. Java membuat object baru di memori, termasuk membuat instance variabel yang bertipe primitive sekalipun.
2. Object lain yang direference oleh class yang diclone tersebut tidak dibuat copynya, hanya membuat reference baru ke object tersebut. Jadi kini ada dua reference yang mengarah pada object yang sama.

public class Car extends Object implements Cloneable {

private String carName;
private String carType;
private SUV suv;

public String getCarName() {
return carName;
}

public void setCarName(String carName) {
this.carName = carName;
}

public String getCarType() {
return carType;
}

public void setCarType(String carType) {
this.carType = carType;
}

public SUV getSuv() {
return suv;
}

public void setSuv(SUV suv) {
this.suv = suv;
}

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}

@Override
public String toString() {
return "carName : " + carName + " car type : " + carType + " suv : " + suv;
}

}

public class SUV {

private String suvType;
private String suvModel;

public String getSuvType() {
return suvType;
}

public void setSuvType(String suvType) {
this.suvType = suvType;
}

public String getSuvModel() {
return suvModel;
}

public void setSuvModel(String suvModel) {
this.suvModel = suvModel;
}

@Override
public String toString() {
return "suv type : " + suvType + " suv model : " + suvModel;
}

}

public class Main {

public static void main(String[] args) {
SUV suv = new SUV();
suv.setSuvModel("suv model 1");
suv.setSuvType("suv type 1");

Car car = new Car();
car.setCarName("Timor");
car.setCarType("Sedan");
car.setSuv(suv);

System.out.println("car 1 : " + car);
try {
Car car2 = (Car) car.clone();
suv.setSuvModel("suv model update");
System.out.println("car 2 : " + car2);
System.out.println("car 1 : " + car);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
car 1 : carName : Timor car type : Sedan suv : suv type : suv type 1 suv model : suv model 1
car 2 : carName : Timor car type : Sedan suv : suv type : suv type 1 suv model : suv model update
car 1 : carName : Timor car type : Sedan suv : suv type : suv type 1 suv model : suv model update

Sedangkan deep copy adalah :
1. Java menduplikasi object yang dicopy termasuk variabel yang primitif.
2. Object yang direference pun dicopy sehingga kini dua object yang diclone merefer ke dua object yang berbeda.

Bagaimana implementasi deep copy pada java? Kita bisa mengoverride method clone() untuk keperluan deep copy ini.

Monday, September 7, 2009

Design Pattern in Brief

Immutable

Immutable berarti tidak dapat diubah. Apanya yang tidak dapat diubah? Tentunya state dari suatu object, secara teknis, tentu nilai dari variabelnya. Dalam design pattern kita bisa membuat sebuah object menjadi immutable dengan cara
1. Semua instance variabel harus diset hanya dari konstruktor. Tidak ada method atau cara lain untuk mengeset nilai variabel selain dari konstruktor tsb. Dengan kata lain jadikan instance variabel private dan tanpa setter.
2. Agar class yang kita inginkan menjadi immutable tidak bisa di-extends, maka kita deklarasikan class tersebut sebagai final. Dengan demikian tidak ada cara untuk mengoverride method yang kita miliki.
3. Semua instance variabel harus dideklarasikan final sehingga hanya bisa diinisialisasi sekali, lewat konstruktor.
4. Jika sebuah method mempunyai return value berupa class dari object tersebut, maka sebaiknya ia tidak mengembalikan object yang sebenarnya melainkan copy atau clonenya.

Monitor

Menulis tentang monitor tentunya mengingatkan pada kuliah Sistem Operasi oleh Bapak Mochammad Husni :). Tak salah lagi, topik yang jadi hot thread pada kuliah itu adalah race condition, deadlock. Monitor adalah mekanisme yang digunakan untuk mengatasi deadlock sehingga hanya satu thread yang bisa mengakses sebuah method dalam satu waktu. Pada Java hal ini diatasi dengan mendeklarasikan sebuah method sebagai synchronized.

Factory



Factory dapat digambarkan sebagai sebuah class yang mengotomatisasi pembuatan sebuah object. Dalam diagram berikut kita memiliki sebuah parent class (bisa abstract, interface, atau konkret class) yang diextend oleh dua subclass. Sebuah class lain, yang bisa jadi bukan bagian dari aplikasi kita, ingin memanfaatkan class yang telah kita buat tersebut. Yang class lain tersebut ketahui hanyalah parent class saja. Mereka tidak tahu menahu struktur hirarki class kita. Sementara itu struktur yang kita buat adalah request harus didelegasikan ke masing-masing subclass yang bersesuaian. Bagaimana kita dapat menginstansiasi class-class yang berbeda dan mengembalikan return value dengan tipe yang sama adalah tugas Class Factory.

Pada gambar kita memiliki dua buah struktur factory. Pertama adalah sebuah interface sebagai contract dengan class lain. Kedua adalah Konkret class yang berisi factory method yang bertugas 'memilih' subclass mana yang harus diinstansiasi. Bagaimana cara factory method melakukan tugasnya? Sederhananya dengan if-then rules atau switch.

Singleton

Dalam design pattern, singleton berarti hanya ada satu instance. Bagaimana cara mendapatkan hanya satu instance selama aplikasi berjalan? Kita bisa melakukannya dengan meletakkan variabel dengan tipe class singleton sebagai global variabel, semua class yang membutuhkan object tersebut merefer ke variabel global yang dimaksud. Namun walaupun demikian, client masih tetap bisa membuat instance dari class tersebut bukan? Walaupun bukan sebagai global variabel.

Sebuah class yang bisa membuat dirinya sendiri hanya mempunyai satu instance disebut singleton class. Berikut adalah cara membuat class menjadi singleton
1. Buat constructornya private. Dengan demikian tidak ada cara class lain menginstansiasi class tersebut selain dirinya sendiri.
2. Di dalam class singleton tersebut buat public static method dengan nama getInstance().
3. Di dalam body method getInstance() buat instance yang hanya dibuat sekali pada saat pertama class di-invoke.

public class Singleton {
private static final Singleton INSTANCE = new Singleton();

// Private constructor prevents instantiation from other classes
private Singleton() {}

public static Singleton getInstance() {
return INSTANCE;
}
}

Pada code di atas, instance singleton dibuat hanya sekali yaitu saat class dipanggil. Namun cara ini memiliki kelemahan yaitu instance dibuat saat pertama class diinisialisasi, bukan saat dipanggil.

public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}

/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}


Code di atas adalah yang diusulkan oleh Bill Pugh. Instance benar-benar dibuat saat getInstance() dipanggil, bukan saat class diinisialisasi.
AbstractFactory

Untuk memahami AbstractFactory perhatikan gambar berikut



















public class LuxuryVehicleFactory extends VehicleFactory {
public Car getCar() {
return new LuxuryCar("L-C");
}
public SUV getSUV() {
return new LuxurySUV("L-S");
}
}

public class NonLuxuryVehicleFactory extends VehicleFactory {
public Car getCar() {
return new NonLuxuryCar("NL-C");
}
public SUV getSUV() {
return new NonLuxurySUV("NL-S");
}
}

public abstract class VehicleFactory {
public static final String LUXURY_VEHICLE = "Luxury";
public static final String NON_LUXURY_VEHICLE = "Non-Luxury";
public abstract Car getCar();
public abstract SUV getSUV();

public static VehicleFactory getVehicleFactory(String type) {
if (type.equals(VehicleFactory.LUXURY_VEHICLE))
return new LuxuryVehicleFactory();
if (type.equals(VehicleFactory.NON_LUXURY_VEHICLE))
return new NonLuxuryVehicleFactory();
return new LuxuryVehicleFactory();
}
}

Wednesday, September 2, 2009

JVM terminated. Exit code=-1

Have you ever got the above error message when you try to launch eclipse? Well it happened yesterday. When I started eclipse, it showed this error window



This error is quiet strange since I did fresh install jdk and eclipse on my windows machine. When I googled this error, there were some advices including to increase Xms512. But it did work before without setting anything. Then finally I found a good advice here : http://www.coderanch.com/t/423852/IDEs-Version-Control-other-tools/IRAD-JVM-Terminated-Exit-Code.

Yup. Remove the entries in eclipse.ini :). But unfortunately after you do that, there will be an error saying that you need to close your eclipse. To deal with this, you need to restore your eclipse.ini file (you can try another eclipse.ini file or just restore the last one) and change this :
-Xmx512m to Xmx256m.

People says that it happens because eclipse tries to allocate heap memory as much as 512 but it fails.

Saturday, August 29, 2009

GWT, A small notes

It seems that developing rich internet application using GWT is very interesting because of its rich of features widgets. But sometimes something annoying and unrealistic happens. Here are just small amount of problems that looks ridiculous and trivial but could give you headache and make you spend hours googling forums.

First thing first, consider this :

public class Skippy implements EntryPoint {
public void onModuleLoad() {

MenuView menu = new MenuView();

TicTacView tictac = new TicTacView();

RootPanel.get("menu").add(menu);

RootPanel.get("tictac").add(tictac);

}

}

Code snippet above is about to add two widgets to RootPanel. Look at this html code snippet :



See the connection? Yup I wanna add a widget in an appropriate html element. You can do this using this html code : <span id="menu"></span>, but not with this one : <span id="menu" />. A bit tricky huh...

Second thing is, folder structure. Suppose I wanna add some new classes in a package, say, LanguageConstants which is used for internationalization. You have to place it inside package that wraps your module (edu.mat.gwt.skippy) and if it is for client side then you have to place it inside client package, for example edu.mat.gwt.skippy.client.util. You can not put it inside edu.mat.gwt.skippy.util because the compiler will say that there is no source code for your class.

Monday, August 17, 2009

PHP? A big no-no

Woww posting terakhir tgl 28 Juli? Blogger macam apa ini tiga minggu nggak ngeblog sama sekali. Maklum sibuk training (biasa... cari kambing hitam dulu). Training kali ini menarik soalnya tentang Java hehehe.

Jadi, training Java sekitar 12 hari dan cukup menarik karena mengharuskan kita belajar spring, hibernate, struts, servlet, deelel. Tapi semua itu berubah karena nilai Javanya ternyata ancurr lebur :(, padahal semua teknologi yang ditrainingkan sudah dipake komplit. Yah semoga trainernya diberi pencerahan, amiin (kesel mode on).

Btw, back to title, setelah Java sekarang dihajar sama materi PHP. Dari awal sudah nggak ada minat mau belajar PHP, karena PHP pendukung kapitalis barat dengan tanda $-nya (becanda boss). Yang bikin kesel dari PHP adalah methodnya yang buaaaanyak sekali dan gak jelas punya siapa alias silakan baca manualnya. Dan fakta mencengangkan lainnya yang baru newbie ini ketahui adalah bahwa kita bisa mengirim parameter ke method yang tidak punya argumen (hah...), catet ni method bukan overloading lho ya... Jadi siap-siap aja bingung kalo nyari method a(param1, param2) tapi yang diketemukan hanya method a().

Sekian dulu deh uneg-uneg dari saya yang pingin dikeluarkan (daripada dipendem trus stres kayak Marshanda, ya kan). Moga-moga aja trainernya gak baca ini dan moga-moga besok ujiannya ada mukjizat...amin
 

©2009 Stay the Same | by TNB