MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Pola Desain untuk Lapisan Akses Data

Nah, pendekatan umum untuk penyimpanan data di java, seperti yang Anda catat, sama sekali tidak berorientasi objek. Ini dengan sendirinya tidak buruk atau baik:"berorientasi objek" bukanlah keuntungan atau kerugian, itu hanya salah satu dari banyak paradigma, yang terkadang membantu dengan desain arsitektur yang baik (dan terkadang tidak).

Alasan DAO di java biasanya tidak berorientasi objek adalah persis apa yang ingin Anda capai - mengurangi ketergantungan Anda pada database tertentu. Dalam bahasa yang dirancang lebih baik, yang memungkinkan pewarisan berganda, ini, atau tentu saja, dapat dilakukan dengan sangat elegan dengan cara berorientasi objek, tetapi dengan Java, sepertinya lebih banyak masalah daripada nilainya.

Dalam arti yang lebih luas, pendekatan non-OO membantu memisahkan data tingkat aplikasi Anda dari cara penyimpanannya. Ini lebih dari (non) ketergantungan pada spesifikasi database tertentu, tetapi juga skema penyimpanan, yang sangat penting ketika menggunakan database relasional (jangan mulai dengan ORM):Anda dapat memiliki skema relasional yang dirancang dengan baik diterjemahkan dengan mulus ke dalam model OO aplikasi oleh DAO Anda.

Jadi, sebagian besar DAO di java saat ini pada dasarnya adalah apa yang Anda sebutkan di awal - kelas, penuh dengan metode statis. Satu perbedaannya adalah, daripada membuat semua metode statis, lebih baik memiliki satu "metode pabrik" statis (mungkin, di kelas yang berbeda), yang mengembalikan instance (tunggal) dari DAO Anda, yang mengimplementasikan antarmuka tertentu , digunakan oleh kode aplikasi untuk mengakses database:

public interface GreatDAO {
    User getUser(int id);
    void saveUser(User u);
}
public class TheGreatestDAO implements GreatDAO {
   protected TheGeatestDAO(){}
   ... 
}
public class GreatDAOFactory {
     private static GreatDAO dao = null;
     protected static synchronized GreatDao setDAO(GreatDAO d) {
         GreatDAO old = dao;
         dao = d;
         return old;
     }
     public static synchronized GreatDAO getDAO() {
         return dao == null ? dao = new TheGreatestDAO() : dao;
     }
}

public class App {
     void setUserName(int id, String name) {
          GreatDAO dao =  GreatDAOFactory.getDao();
          User u = dao.getUser(id);
          u.setName(name);
          dao.saveUser(u);
     }
}

Mengapa melakukannya dengan cara ini sebagai lawan dari metode statis? Nah, bagaimana jika Anda memutuskan untuk beralih ke database yang berbeda? Secara alami, Anda akan membuat kelas DAO baru, menerapkan logika untuk penyimpanan baru Anda. Jika Anda menggunakan metode statis, Anda sekarang harus melalui semua kode Anda, mengakses DAO, dan mengubahnya untuk menggunakan kelas baru Anda, bukan? Ini bisa menjadi rasa sakit yang luar biasa. Dan bagaimana jika Anda berubah pikiran dan ingin beralih kembali ke db lama?

Dengan pendekatan ini, yang perlu Anda lakukan hanyalah mengubah GreatDAOFactory.getDAO() dan buat itu membuat turunan dari kelas yang berbeda, dan semua kode aplikasi Anda akan menggunakan database baru tanpa perubahan apa pun.

Dalam kehidupan nyata, ini sering dilakukan tanpa perubahan kode sama sekali:metode pabrik mendapatkan nama kelas implementasi melalui pengaturan properti, dan membuat instance menggunakan refleksi, jadi, yang perlu Anda lakukan untuk beralih implementasi adalah mengedit properti mengajukan. Sebenarnya ada kerangka kerja - seperti spring atau guice - yang mengelola mekanisme "injeksi ketergantungan" ini untuk Anda, tetapi saya tidak akan menjelaskan lebih lanjut, pertama, karena itu benar-benar di luar cakupan pertanyaan Anda, dan juga, karena saya belum tentu yakin bahwa manfaat yang Anda dapatkan dari menggunakan kerangka kerja tersebut sepadan dengan kesulitan mengintegrasikannya untuk sebagian besar aplikasi.

Manfaat lain (mungkin, lebih mungkin untuk dimanfaatkan) dari "pendekatan pabrik" ini sebagai lawan dari statis adalah testabilitas. Bayangkan, Anda sedang menulis pengujian unit, yang seharusnya menguji logika App . Anda kelas secara independen dari DAO yang mendasarinya. Anda tidak ingin itu menggunakan penyimpanan dasar yang sebenarnya karena beberapa alasan (kecepatan, harus mengaturnya, dan membersihkan kata penutup, kemungkinan tabrakan dengan tes lain, kemungkinan mencemari hasil tes dengan masalah di DAO, tidak terkait dengan App , yang sebenarnya sedang diuji, dll.).

Untuk melakukan ini, Anda menginginkan kerangka kerja pengujian, seperti Mockito , yang memungkinkan Anda untuk "mengejek" fungsionalitas objek atau metode apa pun, menggantinya dengan objek "dummy", dengan perilaku yang telah ditentukan sebelumnya (saya akan melewatkan detailnya, karena, ini lagi-lagi di luar cakupan). Jadi, Anda dapat membuat objek dummy ini menggantikan DAO Anda, dan membuat GreatDAOFactory kembalikan boneka Anda alih-alih yang asli dengan memanggil GreatDAOFactory.setDAO(dao) sebelum tes (dan memulihkannya setelah). Jika Anda menggunakan metode statis alih-alih kelas instance, ini tidak akan mungkin.

Satu lagi manfaat, yang agak mirip dengan beralih database yang saya jelaskan di atas adalah "mucikari" dao Anda dengan fungsionalitas tambahan. Misalkan aplikasi Anda menjadi lebih lambat saat jumlah data dalam database bertambah, dan Anda memutuskan bahwa Anda memerlukan lapisan cache. Menerapkan kelas pembungkus, yang menggunakan instance dao nyata (disediakan sebagai param konstruktor) untuk mengakses database, dan menyimpan objek yang dibacanya di memori, sehingga objek tersebut dapat dikembalikan lebih cepat. Anda kemudian dapat membuat GreatDAOFactory.getDAO instantiate pembungkus ini, agar aplikasi dapat memanfaatkannya.

(Ini disebut "pola delegasi" ... dan sepertinya sulit, terutama ketika Anda memiliki banyak metode yang ditentukan dalam DAO Anda:Anda harus menerapkan semuanya dalam pembungkus, bahkan untuk mengubah perilaku hanya satu Sebagai alternatif, Anda bisa mensubklasifikasikan dao Anda, dan menambahkan caching ke dalamnya dengan cara ini. Ini akan menjadi pengkodean yang jauh lebih membosankan di awal, tetapi mungkin menjadi masalah ketika Anda memutuskan untuk mengubah database, atau, lebih buruk lagi, memiliki opsi untuk beralih implementasi bolak-balik.)

Salah satu alternatif yang sama-sama digunakan (tetapi, menurut saya, lebih rendah) dari metode "pabrik" adalah membuat dao variabel anggota di semua kelas yang membutuhkannya:

public class App {
   GreatDao dao;
   public App(GreatDao d) { dao = d; }
}

Dengan cara ini, kode yang membuat instance kelas-kelas ini perlu membuat instance objek dao (masih dapat menggunakan pabrik), dan menyediakannya sebagai parameter konstruktor. Kerangka kerja injeksi ketergantungan yang saya sebutkan di atas, biasanya melakukan sesuatu yang mirip dengan ini.

Ini memberikan semua manfaat dari pendekatan "metode pabrik", yang saya jelaskan sebelumnya, tetapi, seperti yang saya katakan, menurut saya tidak sebaik itu. Kerugiannya di sini adalah harus menulis konstruktor untuk setiap kelas aplikasi Anda, melakukan hal yang persis sama berulang-ulang, dan juga tidak dapat membuat instance kelas dengan mudah saat dibutuhkan, dan beberapa kehilangan keterbacaan:dengan basis kode yang cukup besar , pembaca kode Anda, yang tidak terbiasa dengannya, akan kesulitan memahami implementasi dao yang sebenarnya digunakan, bagaimana itu dipakai, apakah itu singleton, implementasi thread-safe, apakah itu menyimpan status, atau cache apa saja, bagaimana keputusan untuk memilih implementasi tertentu dibuat, dll.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB C++, Bagaimana cara menambahkan nilai ISODate saat memasukkan

  2. Dapatkan ukuran semua dokumen dalam kueri

  3. Di Mongo apa perbedaan antara $near dan $nearSphere?

  4. Di Mongoose Model.find() dan Model.find().exec() menghasilkan hasil yang sama. Jadi mengapa repot-repot menggunakan Model.find().exec()?

  5. Modul tidak ditemukan:Kesalahan:Tidak dapat menyelesaikan 'dns' saat menggunakan MongoDB