Android Design Pattern: Observer Pattern
() translation by (you can also view the original English article)
Apa yang dimaksud dengan Observer Pattern?
Observer Pattern adalah software design pattern yang menetapkan one-to-many dependensi antara objek. Kapan saja keadaan salah satu objek ("subject" atau "observable, demikianlah") perubahan, semua objek-objek lain ("observer") yang bergantung di atasnya akan diberitahu.
Mari kita gunakan contoh dari pengguna yang telah berlangganan untuk menerima penawaran dari Envato Market melalui email. Pengguna dalam kasus ini adalah pengamat. Kapan saja ada tawaran dari Envato pasar, mereka mendapatkan pemberitahuan tentang hal ini melalui email. Setiap pengguna dapat kemudian membeli ke Penawaran atau memutuskan bahwa mereka mungkin tidak akan benar-benar tertarik pada saat itu. Pengguna (observer) juga dapat berlangganan untuk menerima penawaran dari pasar e-commerce lain jika mereka inginkan dan kemudian benar-benar mungkin berhenti berlangganan dari menerima tawaran dari salah satu dari mereka.
Pattern ini sangat mirip dengan Publish-Subcribe pattern. Subjek atau observable menerbitkan keluar pemberitahuan untuk para observer tergantung tanpa bahkan mengetahui berapa banyak pengamat telah berlangganan itu, atau siapa mereka — diamati hanya tahu bahwa mereka harus menerapkan interface (kita akan segara kesana), tanpa khawatir tentang apa tindakan para pengamat mungkin melakukan.
Manfaat dari Observer Pattern
- Subjek tahu sedikit tentang para observer. Satu-satunya yang tahu adalah bahwa pengamat menerapkan atau menyetujui kontrak tertentu atau interface.
- Subjek dapat digunakan kembali tanpa melibatkan observer mereka, dan yang sama berlaku untuk observer terlalu.
- Tidak ada modifikasi ini dilakukan untuk subjek untuk mengakomodasi observer yang baru. Observer baru hanya perlu untuk mengimplementasikan inteface yang subjek sadar dan kemudian mendaftar untuk subjek.
- Observer bisa terdaftar lebih dari satu subjek yang terdaftar.
Semua manfaat ini memberi Anda longgar, kopling antara modul dalam kode Anda, yang memungkinkan Anda untuk membangun desain yang fleksibel untuk aplikasi Anda. Dalam sisa dari posting ini, kita akan melihat bagaimana untuk membuat kita sendiri Observer pattern implementasi, dan kami juga akan menggunakan built-in Java Observer/Observable API serta melihat ke pustaka pihak ketiga yang dapat menawarkan fungsionalitas seperti itu.
Membangun Observer Pattern kita sendiri
1. Buat Interface subjek
Kita mulai dengan mendefinisikan sebuah inteface yang akan menerapkan (observables).
1 |
public interface Subject { |
2 |
void registerObserver(RepositoryObserver repositoryObserver); |
3 |
void removeObserver(RepositoryObserver repositoryObserver); |
4 |
void notifyObservers(); |
5 |
}
|
Pada kode diatas, kita menciptakan Java Interface dengan tiga metode. RegisterObserver()
metode pertama, seperti yang dikatakan, akan mendaftarkan observer jenis RepositoryObserver
(kami akan membuat interface tersebut segera) kepada subjek. removeObserver()
akan dipanggil untuk menghapus observer yang ingin berhenti mendapatkan pemberitahuan dari subjek, dan akhirnya, notifyObserver()
akan mengirim siaran ke semua observer setiap kali ada perubahan. Sekarang, mari kita membuat sebuah kelas concrete subjek yang akan mengimplementasikan interface subjek yang kami telah membuat:
1 |
import android.os.Handler; |
2 |
import java.util.ArrayList; |
3 |
|
4 |
public class UserDataRepository implements Subject { |
5 |
private String mFullName; |
6 |
private int mAge; |
7 |
private static UserDataRepository INSTANCE = null; |
8 |
|
9 |
private ArrayList<RepositoryObserver> mObservers; |
10 |
|
11 |
private UserDataRepository() { |
12 |
mObservers = new ArrayList<>(); |
13 |
getNewDataFromRemote(); |
14 |
}
|
15 |
|
16 |
// Simulate network
|
17 |
private void getNewDataFromRemote() { |
18 |
final Handler handler = new Handler(); |
19 |
handler.postDelayed(new Runnable() { |
20 |
@Override
|
21 |
public void run() { |
22 |
setUserData("Chike Mgbemena", 101); |
23 |
}
|
24 |
}, 10000); |
25 |
}
|
26 |
|
27 |
// Creates a Singleton of the class
|
28 |
public static UserDataRepository getInstance() { |
29 |
if(INSTANCE == null) { |
30 |
INSTANCE = new UserDataRepository(); |
31 |
}
|
32 |
return INSTANCE; |
33 |
}
|
34 |
|
35 |
@Override
|
36 |
public void registerObserver(RepositoryObserver repositoryObserver) { |
37 |
if(!mObservers.contains(repositoryObserver)) { |
38 |
mObservers.add(repositoryObserver); |
39 |
}
|
40 |
}
|
41 |
|
42 |
@Override
|
43 |
public void removeObserver(RepositoryObserver repositoryObserver) { |
44 |
if(mObservers.contains(repositoryObserver)) { |
45 |
mObservers.remove(repositoryObserver); |
46 |
}
|
47 |
}
|
48 |
|
49 |
@Override
|
50 |
public void notifyObservers() { |
51 |
for (RepositoryObserver observer: mObservers) { |
52 |
observer.onUserDataChanged(mFullName, mAge); |
53 |
}
|
54 |
}
|
55 |
|
56 |
public void setUserData(String fullName, int age) { |
57 |
mFullName = fullName; |
58 |
mAge = age; |
59 |
notifyObservers(); |
60 |
}
|
61 |
}
|
Kelas atas mengimplementasikan interface Subjek
. Kami memiliki ArrayList
yang memegang pengamat dan kemudian menciptakan dalam konstruktor pribadi. Observer register oleh ditambahkan ke ArrayList
dan demikian juga, unregisters oleh dikeluarkan dari ArrayList
.
Perhatikan bahwa kita adalah simulasi jaringan permintaan untuk mengambil data baru. Setelah metode setUserData() dipanggil dan diberikan nilai baru untuk nama dan usia, kita memanggil metode notifyObservers() yang, seperti dikatakan, memberitahu atau mengirim broadcast ke semua terdaftar observer tentang perubahan data baru. Nilai-nilai baru untuk nama lengkap dan umur juga dilalui sepanjang. Hal ini dapat memiliki beberapa pengamat tetapi, dalam tutorial ini, kita akan membuat hanya satu pengamat. Tapi pertama-tama, mari kita membuat observer interface.
2. membuat Observer Interface
1 |
public interface RepositoryObserver { |
2 |
void onUserDataChanged(String fullname, int age); |
3 |
}
|
Pada kode diatas, kita menciptakan observer interface yang harus menerapkan. Hal ini memungkinkan kode kami untuk menjadi lebih fleksibel karena kami pengkodean untuk antarmuka bukan sebuah implementasi konkrit. Kelas Subject
konkrit tidak perlu menyadari banyak interface konkrit yang mungkin memiliki; semua tahu tentang mereka adalah bahwa mereka mengimplementasikan antarmuka RepositoryObserver
.
Mari kita membuat sebuah kelas konkrit yang mengimplementasikan interface ini.
1 |
import android.os.Bundle; |
2 |
import android.support.v7.app.AppCompatActivity; |
3 |
import android.widget.TextView; |
4 |
|
5 |
public class UserProfileActivity extends AppCompatActivity implements RepositoryObserver { |
6 |
private Subject mUserDataRepository; |
7 |
private TextView mTextViewUserFullName; |
8 |
private TextView mTextViewUserAge; |
9 |
|
10 |
@Override
|
11 |
protected void onCreate(Bundle savedInstanceState) { |
12 |
super.onCreate(savedInstanceState); |
13 |
setContentView(R.layout.activity_user_profile); |
14 |
|
15 |
mTextViewUserAge = (TextView) findViewById(R.id.tv_age); |
16 |
mTextViewUserFullName = (TextView) findViewById(R.id.tv_fullname); |
17 |
|
18 |
mUserDataRepository = UserDataRepository.getInstance(); |
19 |
mUserDataRepository.registerObserver(this); |
20 |
}
|
21 |
|
22 |
@Override
|
23 |
public void onUserDataChanged(String fullname, int age) { |
24 |
mTextViewUserFullName.setText(fullname); |
25 |
mTextViewUserAge.setText(age); |
26 |
}
|
27 |
|
28 |
@Override
|
29 |
protected void onDestroy() { |
30 |
super.onDestroy(); |
31 |
mUserDataRepository.removeObserver(this); |
32 |
}
|
33 |
}
|
Hal pertama untuk melihat kode diatas adalah bahwa UserProfileActivity
mengimplementasikan interface RepositoryObserver
— sehingga menerapkan metode onUserDataChanged()
. Dalam metode onCreate()
activity, kami mendapat instance UserDataRepository
yang kita kemudian diinisialisasi dan akhirnya terdaftar ini observer.
Dalam metode onDestroy()
, kami ingin berhenti mendapatkan pemberitahuan, jadi kami membatalkan registrasi dari menerima pemberitahuan.
Dalam metode onUserDataChanged()
, kita ingin memperbarui widget TextView
-mTextViewUserFullName
dan mTextViewUserAge
— dengan set baru nilai data.
Sekarang kita hanya memiliki satu kelas observer, tetapi mungkin dan mudah bagi kita untuk membuat kelas-kelas lain yang ingin menjadi observer kelas UserDataRepository
. Sebagai contoh, kita bisa dengan mudah memiliki SettingsActivity
yang ingin juga diberitahu tentang perubahan data pengguna dengan menjadi observer.
Push dan Pull model
Dalam contoh di atas, kita menggunakan model push observer pattern. Dalam model ini, subjek akan memberitahu para observer tentang perubahan oleh lewat data yang berubah. Tetapi dalam pull model, subjek masih akan memberitahu para observer, tetapi tidak benar-benar lulus data yang berubah. Para observer kemudian pull data yang mereka butuhkan setelah mereka menerima pemberitahuan.
Memanfaatkan Observer Built-In Java API
Sejauh ini, kami telah menciptakan implementasi Observer Pattern kita sendiri, tetapi Java memiliki built-in Observer / Observable dukungan dalam API. Dalam bagian ini, kita akan menggunakan ini. API ini menyederhanakan beberapa implementasi, seperti yang Anda lihat.
1. Buat Observable
UserDataRepository
kami — yang merupakan subjek atau observable kami — sekarang akan extend superclass java.util.Observable
menjadi Observable. Ini adalah kelas yang ingin diamati oleh observer satu atau lebih.
1 |
import android.os.Handler; |
2 |
import java.util.Observable; |
3 |
|
4 |
public class UserDataRepository extends Observable { |
5 |
private String mFullName; |
6 |
private int mAge; |
7 |
private static UserDataRepository INSTANCE = null; |
8 |
|
9 |
private UserDataRepository() { |
10 |
getNewDataFromRemote(); |
11 |
}
|
12 |
|
13 |
// Returns a single instance of this class, creating it if necessary.
|
14 |
public static UserDataRepository getInstance() { |
15 |
if(INSTANCE == null) { |
16 |
INSTANCE = new UserDataRepository(); |
17 |
}
|
18 |
return INSTANCE; |
19 |
}
|
20 |
|
21 |
// Simulate network
|
22 |
private void getNewDataFromRemote() { |
23 |
final Handler handler = new Handler(); |
24 |
handler.postDelayed(new Runnable() { |
25 |
@Override
|
26 |
public void run() { |
27 |
setUserData("Mgbemena Chike", 102); |
28 |
}
|
29 |
}, 10000); |
30 |
}
|
31 |
|
32 |
public void setUserData(String fullName, int age) { |
33 |
mFullName = fullName; |
34 |
mAge = age; |
35 |
setChanged(); |
36 |
notifyObservers(); |
37 |
}
|
38 |
|
39 |
public String getFullName() { |
40 |
return mFullName; |
41 |
}
|
42 |
|
43 |
public int getAge() { |
44 |
return mAge; |
45 |
}
|
46 |
}
|
Sekarang bahwa kita telah direfractor kelas UserDataRepository
kami untuk menggunakan API diamati Java, mari kita melihat apa yang berubah dibandingkan dengan versi sebelumnya. Hal pertama yang menyadari adalah bahwa kita extend kelas super (ini berarti bahwa kelas ini tidak bisa extend setiap kelas lain) dan tidak melaksanakan interface seperti yang kita lakukan di bagian sebelumnya.
Kami tidak lagi memegang ArrayList
observer; ini ditangani di kelas super. Demikian pula, kita tidak perlu khawatir tentang pendaftaran, penghapusan, atau pemberitahuan observers—java.util.Observable
menangani semua itu bagi kita.
Perbedaan lain adalah bahwa di kelas ini kami mempekerjakan pull style. Kami mengingatkan para observer bahwa perubahan yang telah terjadi dengan notifyObservers()
, tetapi para observer akan perlu untuk menarik data menggunakan Getter bidang yang kita telah didefinisikan di kelas ini. Jika Anda ingin menggunakan push style sebaliknya, maka Anda dapat menggunakan metode notifyObservers(Object arg)
dan parsing yang diubah data ke observer di argumen objek.
Metode setChanged()
kelas super menetapkan tanda ke true, menunjukkan bahwa data telah berubah. Kemudian Anda dapat memanggil metode notifyObservers().
Perlu diketahui bahwa jika Anda tidak memanggil setChanged()
sebelum memanggil notifyObsevers(),
observer tidak akan diberitahu. Anda dapat memeriksa nilai falg ini dengan menggunakan hasChanged()
metode dan jelas itu kembali ke false dengan clearChanged().
Sekarang bahwa kita memiliki kelas kami observable dibuat, mari kita lihat bagaimana mengatur observer juga.
2. Membuat Observer
Kelas observable UserDataRepository
kami membutuhkan seorang Observer yang sesuai untuk menjadi berguna, jadi mari kita refactor kami UserProfileActivity
untuk mengimplementasikan interface java.util.Observer.
1 |
import android.os.Bundle; |
2 |
import android.support.v7.app.AppCompatActivity; |
3 |
import android.widget.TextView; |
4 |
import com.chikeandroid.tutsplusobserverpattern.R; |
5 |
import java.util.Observable; |
6 |
import java.util.Observer; |
7 |
|
8 |
public class UserProfileActivity extends AppCompatActivity implements Observer { |
9 |
private Observable mUserDataRepositoryObservable; |
10 |
private TextView mTextViewUserFullName; |
11 |
private TextView mTextViewUserAge; |
12 |
|
13 |
@Override
|
14 |
protected void onCreate(Bundle savedInstanceState) { |
15 |
super.onCreate(savedInstanceState); |
16 |
setContentView(R.layout.activity_user_profile); |
17 |
|
18 |
mTextViewUserAge = (TextView) findViewById(R.id.tv_age); |
19 |
mTextViewUserFullName = (TextView) findViewById(R.id.tv_fullname); |
20 |
|
21 |
mUserDataRepositoryObservable = UserDataRepository.getInstance(); |
22 |
mUserDataRepositoryObservable.addObserver(this); |
23 |
}
|
24 |
|
25 |
@Override
|
26 |
public void update(Observable observable, Object o) { |
27 |
if (observable instanceof UserDataRepository) { |
28 |
UserDataRepository userDataRepository = (UserDataRepository)observable; |
29 |
mTextViewUserAge.setText(String.valueOf(userDataRepository.getAge())); |
30 |
mTextViewUserFullName.setText(userDataRepository.getFullName()); |
31 |
}
|
32 |
}
|
33 |
|
34 |
@Override
|
35 |
protected void onDestroy() { |
36 |
super.onDestroy(); |
37 |
mUserDataRepositoryObservable.deleteObserver(this); |
38 |
}
|
39 |
}
|
Dalam metode onCreate()
, kami menambahkan kelas ini sebagai pengamat ke UserDataRepository
observable dengan menggunakan metode addObserver()
di kelas super java.util.Observable.
Dalam metode update()
akan membuat yang observer harus menerapkan, kami memeriksa jika Observable
kita menerima sebagai parameter adalah instance dari UserDataRepository
kami (Perhatikan bahwa observer dapat berlangganan berbeda observables), dan kemudian kita melemparkannya bahwa contoh dan mengambil nilai-nilai yang kita inginkan menggunakan Getter bidang. Kemudian kita menggunakan nilai-nilai tersebut untuk memperbarui tampilan widget.
Ketika activity hancur, kita tidak perlu untuk mendapatkan setiap update dari diamati, jadi kami hanya akan menghapus aktivitas dari daftar pengamat dengan memanggil metode deleteObserver().
Perpustakaan untuk menerapkan Observer Pattern
Jika Anda tidak ingin untuk membangun implementasi Observer Pattern Anda sendiri dari awal atau menggunakan Java Observer API, Anda dapat menggunakan beberapa bebas dan open source perpustakaan yang tersedia untuk Android seperti Greenrobot's EventBus. Untuk mempelajari lebih lanjut tentang hal itu, memeriksa tutorial saya di sini di Envato Tuts +.
Atau, Anda mungkin seperti RxAndroid dan RxJava. Pelajari lebih banyak tentang mereka di sini:
Kesimpulan
Dalam tutorial ini, Anda belajar tentang pola Observer di Java: apa itu, manfaat menggunakan itu, bagaimana untuk menerapkan Anda sendiri, menggunakan API Observer Java, dan juga beberapa librari pihak ketiga untuk melaksanakan pola ini.
Sementara itu, memeriksa beberapa kami kursus dan tutorial pada bahasa Java dan pengembangan aplikasi Android!
- Android SDKRxJava 2 untuk aplikasi Android: RxBinding dan RxLifecycleJessica Thornsby
- Android SDKPraktis Concurrency Android dengan HaMeRMegali timah
- AndroidMemastikan kualitas tinggi Android kode dengan alat-alat analisis statisChike Mgbemena
- Android SDKMembuat aplikasi cerdas dengan Google Cloud Speech dan Natural Language APIAshraff Hathibelagal