CodeGym/Java Blog/Acak/Apa itu anti-pola? Mari kita lihat beberapa contoh (Bagia...
John Squirrels
Level 41
San Francisco

Apa itu anti-pola? Mari kita lihat beberapa contoh (Bagian 1)

Dipublikasikan di grup Acak
anggota
Hari baik untuk semua! Suatu hari saya melakukan wawancara kerja, dan saya ditanyai beberapa pertanyaan tentang anti-pola: apa itu, apa jenisnya, dan apa contoh praktisnya. Tentu saja, saya menjawab pertanyaan itu, tetapi sangat dangkal, karena saya belum pernah mendalami topik ini sebelumnya. Setelah wawancara, saya mulai menjelajahi Internet dan semakin membenamkan diri dalam topik tersebut. Apa itu anti-pola?  Mari kita lihat beberapa contoh (Bagian 1) - 1 Hari ini saya ingin memberikan ikhtisar singkat tentang anti-pola paling populer dan mengulas beberapa contoh. Saya harap membaca ini akan memberi Anda pengetahuan yang Anda butuhkan di bidang ini. Mari kita mulai! Sebelum kita membahas apa itu anti-pattern, mari kita ingat apa itu design pattern. Pola desainadalah solusi arsitektur berulang untuk masalah atau situasi umum yang muncul saat merancang aplikasi. Tapi hari ini kita tidak berbicara tentang mereka, melainkan kebalikannya — anti-pola. Anti -pola adalah pendekatan yang tersebar luas tetapi tidak efektif, berisiko, dan/atau tidak produktif untuk memecahkan kelas masalah umum. Dengan kata lain, ini adalah pola kesalahan (terkadang juga disebut jebakan). Sebagai aturan, anti-pola dibagi menjadi beberapa tipe berikut:
  1. Anti-pola arsitektural — Anti-pola ini muncul ketika struktur suatu sistem dirancang (umumnya oleh seorang arsitek).
  2. Anti-pola manajemen/organisasi — Ini adalah anti-pola dalam manajemen proyek, biasanya ditemui oleh berbagai manajer (atau kelompok manajer).
  3. Anti-pola pengembangan — Anti-pola ini muncul saat sistem diimplementasikan oleh pemrogram biasa.
Berbagai macam anti-pola jauh lebih eksotis, tetapi kami tidak akan mempertimbangkan semuanya hari ini. Untuk pengembang biasa, itu terlalu berlebihan. Sebagai permulaan, mari pertimbangkan anti-pola manajemen sebagai contoh.

1. Kelumpuhan analitis

Kelumpuhan analisisdianggap sebagai anti-pola manajemen klasik. Ini melibatkan analisis situasi yang berlebihan selama perencanaan, sehingga tidak ada keputusan atau tindakan yang diambil, yang pada dasarnya melumpuhkan proses pembangunan. Ini sering terjadi ketika tujuannya adalah untuk mencapai kesempurnaan dan benar-benar mempertimbangkan segalanya selama periode analisis. Anti-pola ini dicirikan dengan berjalan dalam lingkaran (loop tertutup run-of-the-mill), merevisi dan membuat model terperinci, yang pada gilirannya mengganggu alur kerja. Misalnya, Anda mencoba memprediksi hal-hal pada suatu level: tetapi bagaimana jika pengguna tiba-tiba ingin membuat daftar karyawan berdasarkan huruf keempat dan kelima dari nama mereka, termasuk daftar proyek yang paling banyak menghabiskan waktu kerja mereka antara Tahun Baru dan Hari Perempuan Internasional selama empat tahun terakhir? Intinya, itu' terlalu banyak analisis. Berikut adalah beberapa tip untuk melawan kelumpuhan analisis:
  1. Anda perlu mendefinisikan tujuan jangka panjang sebagai suar untuk pengambilan keputusan, sehingga setiap keputusan Anda membawa Anda lebih dekat ke tujuan daripada menyebabkan Anda mandek.
  2. Jangan berkonsentrasi pada hal-hal sepele (mengapa membuat keputusan tentang detail yang tidak penting seolah-olah itu adalah keputusan terpenting dalam hidup Anda?)
  3. Tetapkan tenggat waktu untuk sebuah keputusan.
  4. Jangan mencoba menyelesaikan tugas dengan sempurna — lebih baik melakukannya dengan sangat baik.
Tidak perlu masuk terlalu dalam di sini, jadi kami tidak akan mempertimbangkan anti-pola manajerial lainnya. Oleh karena itu, tanpa pengenalan apa pun, kami akan beralih ke beberapa anti-pola arsitektur, karena artikel ini kemungkinan besar akan dibaca oleh pengembang masa depan daripada manajer.

2. Tuhan keberatan

Objek Tuhan adalah anti-pola yang menggambarkan konsentrasi berlebihan dari semua jenis fungsi dan sejumlah besar data yang berbeda (objek yang berputar di sekitar aplikasi). Ambil contoh kecil:
public class SomeUserGodObject {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";
   private static final String FIND_ALL_CUSTOMERS = "SELECT id, u.email, u.phone, u.first_name_en, u.middle_name_en, u.last_name_en, u.created_date" +
           "  WHERE u.id IN (SELECT up.user_id FROM user_permissions up WHERE up.permission_id = ?)";
   private static final String FIND_BY_EMAIL = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_dateFROM users WHERE email = ?";
   private static final String LIMIT_OFFSET = " LIMIT ? OFFSET ?";
   private static final String ORDER = " ORDER BY ISNULL(last_name_en), last_name_en, ISNULL(first_name_en), first_name_en, ISNULL(last_name_ru), " +
           "last_name_ru, ISNULL(first_name_ru), first_name_ru";
   private static final String CREATE_USER_EN = "INSERT INTO users(id, phone, email, first_name_en, middle_name_en, last_name_en, created_date) " +
           "VALUES (?, ?, ?, ?, ?, ?, ?)";
   private static final String FIND_ID_BY_LANG_CODE = "SELECT id FROM languages WHERE lang_code = ?";
                                  ........
   private final JdbcTemplate jdbcTemplate;
   private Map<String, String> firstName;
   private Map<String, String> middleName;
   private Map<String, String> lastName;
   private List<Long> permission;
                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query( FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }
   @Override
   public Optional<List<User>> findAllEnByEmail(String email) {
       var query = FIND_ALL_USERS_EN + FIND_BY_EMAIL + ORDER;
       return Optional.ofNullable(jdbcTemplate.query(query, userRowMapper(), email));
   }
                              .............
   private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
       switch (type) {
           case USERS:
               return findAllEnUsers(permissionId);
           case CUSTOMERS:
               return findAllEnCustomers(permissionId);
           default:
               return findAllEn();
       }
   }
                              ..............private RowMapper<User> userRowMapperEn() {
       return (rs, rowNum) ->
               User.builder()
                       .id(rs.getLong("id"))
                       .email(rs.getString("email"))
                       .accessFailed(rs.getInt("access_counter"))
                       .createdDate(rs.getObject("created_date", LocalDateTime.class))
                       .firstName(rs.getString("first_name_en"))
                       .middleName(rs.getString("middle_name_en"))
                       .lastName(rs.getString("last_name_en"))
                       .phone(rs.getString("phone"))
                       .build();
   }
}
Di sini kita melihat kelas besar yang melakukan segalanya. Ini berisi kueri basis data serta beberapa data. Kami juga melihat metode fasad findAllWithoutPageEn, yang menyertakan logika bisnis. Objek Tuhan seperti itu menjadi sangat besar dan canggung untuk dipertahankan dengan baik. Kita harus mengotak-atiknya di setiap bagian kode. Banyak komponen sistem yang mengandalkannya dan terkait erat dengannya. Menjadi semakin sulit untuk mempertahankan kode seperti itu. Dalam kasus seperti itu, kode harus dipecah menjadi kelas-kelas terpisah, yang masing-masing hanya memiliki satu tujuan. Dalam contoh ini, kita dapat membagi objek God menjadi kelas Dao:
public class UserDaoImpl {
   private static final String FIND_ALL_USERS_EN = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users;
   private static final String FIND_BY_ID = "SELECT id, email, phone, first_name_en, access_counter, middle_name_en, last_name_en, created_date FROM users WHERE id = ?";

                                   ........
   private final JdbcTemplate jdbcTemplate;

                                   ........
   @Override
   public List<User> findAllEnCustomers(Long permissionId) {
       return jdbcTemplate.query(FIND_ALL_CUSTOMERS + ORDER, userRowMapper(), permissionId);
   }
   @Override
   public List<User> findAllEn() {
       return jdbcTemplate.query(FIND_ALL_USERS_EN + ORDER, userRowMapper());
   }

                               ........
}
Kelas yang berisi data dan metode untuk mengakses data:
public class UserInfo {
   private Map<String, String> firstName;..
   public Map<String, String> getFirstName() {
       return firstName;
   }
   public void setFirstName(Map<String, String> firstName) {
       this.firstName = firstName;
   }
                    ....
Dan akan lebih tepat untuk memindahkan metode dengan logika bisnis ke layanan:
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
   switch (type) {
       case USERS:
           return findAllEnUsers(permissionId);
       case CUSTOMERS:
           return findAllEnCustomers(permissionId);
       default:
           return findAllEn();
   }
}

3. Lajang

Singleton adalah pola yang paling sederhana. Ini memastikan bahwa dalam aplikasi single-threaded akan ada satu instance kelas, dan menyediakan titik akses global ke objek ini. Tapi apakah itu pola atau anti-pola? Mari kita lihat kerugian dari pola ini:
  1. Status global Ketika kita mengakses instance kelas, kita tidak mengetahui status kelas saat ini. Kami tidak tahu siapa yang mengubahnya atau kapan. Keadaan mungkin tidak seperti yang kita harapkan. Dengan kata lain, kebenaran bekerja dengan singleton bergantung pada urutan aksesnya. Ini berarti bahwa subsistem saling bergantung satu sama lain dan, akibatnya, desain menjadi lebih kompleks.

  2. Singleton melanggar prinsip SOLID — prinsip tanggung jawab tunggal: selain tugas langsungnya, kelas singleton juga mengontrol jumlah instance.

  3. Ketergantungan kelas biasa pada singleton tidak terlihat di antarmuka kelas. Karena instance tunggal biasanya tidak diteruskan sebagai argumen metode, melainkan diperoleh langsung melalui getInstance(), Anda perlu masuk ke implementasi setiap metode untuk mengidentifikasi ketergantungan kelas pada singleton — cukup dengan melihat publik kelas kontrak tidak cukup.

    Kehadiran singleton mengurangi testabilitas aplikasi secara keseluruhan dan kelas yang menggunakan singleton pada khususnya. Pertama-tama, Anda tidak dapat mengganti singleton dengan objek tiruan. Kedua, jika singleton memiliki antarmuka untuk mengubah statusnya, maka pengujian akan bergantung satu sama lain.

    Dengan kata lain, singleton meningkatkan kopling, dan semua yang disebutkan di atas tidak lebih dari konsekuensi dari peningkatan kopling.

    Dan jika Anda memikirkannya, Anda dapat menghindari penggunaan singleton. Misalnya, sangat mungkin (dan memang perlu) untuk menggunakan berbagai jenis pabrik untuk mengontrol jumlah instance dari suatu objek.

    Bahaya terbesar terletak pada upaya untuk membangun seluruh arsitektur aplikasi berdasarkan lajang. Ada banyak alternatif bagus untuk pendekatan ini. Contoh terpenting adalah Spring, yaitu wadah IoC-nya: mereka adalah solusi alami untuk masalah pengendalian pembuatan layanan, karena mereka sebenarnya adalah "pabrik steroid".

    Banyak perdebatan yang tak berkesudahan dan tidak dapat didamaikan sekarang berkecamuk tentang hal ini. Terserah Anda untuk memutuskan apakah singleton adalah pola atau anti-pola.

    Kami tidak akan berlama-lama di atasnya. Sebagai gantinya, kita akan beralih ke pola desain terakhir untuk hari ini — poltergeist.

4. Hantu

Poltergeist adalah anti-pola yang melibatkan kelas tidak berguna yang digunakan untuk memanggil metode kelas lain atau hanya menambahkan lapisan abstraksi yang tidak perlu . Anti-pola ini memanifestasikan dirinya sebagai objek berumur pendek, tanpa status. Objek ini sering digunakan untuk menginisialisasi objek lain yang lebih permanen.
public class UserManager {
   private UserService service;
   public UserManager(UserService userService) {
       service = userService;
   }
   User createUser(User user) {
       return service.create(user);
   }
   Long findAllUsers(){
       return service.findAll().size();
   }
   String findEmailById(Long id) {
       return service.findById(id).getEmail();}
   User findUserByEmail(String email) {
       return service.findByEmail(email);
   }
   User deleteUserById(Long id) {
       return service.delete(id);
   }
}
Mengapa kita membutuhkan objek yang hanya sebagai perantara dan mendelegasikan pekerjaannya kepada orang lain? Kami menghilangkannya dan mentransfer fungsionalitas kecil yang dimilikinya ke objek yang berumur panjang. Selanjutnya, kita beralih ke pola yang paling menarik bagi kita (sebagai pengembang biasa), yaitu development anti-patterns .

5. Pengodean keras

Jadi kita sampai pada kata yang mengerikan ini: hard coding. Inti dari anti-pola ini adalah bahwa kode tersebut sangat terikat pada konfigurasi perangkat keras dan/atau lingkungan sistem tertentu. Ini sangat memperumit porting kode ke konfigurasi lain. Anti-pola ini terkait erat dengan angka ajaib (anti-pola ini sering terjalin). Contoh:
public Connection buildConnection() throws Exception {
   Class.forName("com.mysql.cj.jdbc.Driver");
   connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8&characterSetResults=UTF-8&serverTimezone=UTC", "user01", "12345qwert");
   return connection;
}
Sakit, bukan? Di sini kami membuat kode keras pengaturan koneksi kami. Akibatnya, kode hanya akan berfungsi dengan benar dengan MySQL. Untuk mengubah basis data, kita perlu menyelami kode dan mengubah semuanya secara manual. Solusi yang baik adalah dengan meletakkan konfigurasi di file terpisah:
spring:
  datasource:
    jdbc-url:jdbc:mysql://localhost:3306/someDb?characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username:  user01
    password:  12345qwert
Pilihan lain adalah menggunakan konstanta.

6. Jangkar kapal

Dalam konteks anti-patterns, jangkar kapal berarti menjaga bagian-bagian dari sistem yang tidak lagi digunakan setelah melakukan beberapa optimasi atau refactoring. Selain itu, beberapa bagian kode dapat disimpan "untuk penggunaan di masa mendatang" kalau-kalau Anda tiba-tiba membutuhkannya. Pada dasarnya, ini mengubah kode Anda menjadi tempat sampah. Contoh:
public User update(Long id, User request) {
   User user = mergeUser(findById(id), request);
   return userDAO.update(user);
}
private User mergeUser(User findUser, User requestUser) {
   return new User(
           findUser.getId(),
           requestUser.getEmail() != null ? requestUser.getEmail() : findUser.getEmail(),
           requestUser.getFirstName() != null ? requestUser.getFirstName() : findUser.getFirstNameRu(),
           requestUser.getMiddleName() != null ? requestUser.getMiddleName() : findUser.getMiddleNameRu(),
           requestUser.getLastName() != null ? requestUser.getLastName() : findUser.getLastNameEn(),
           requestUser.getPhone() != null ? requestUser.getPhone() : findUser.getPhone());
}
Kami memiliki metode pembaruan yang menggunakan metode terpisah untuk menggabungkan data pengguna dari database dengan data pengguna yang diteruskan ke metode (jika pengguna yang diteruskan ke metode pembaruan memiliki bidang nol, maka nilai bidang lama diambil dari basis data) . Kemudian misalkan ada persyaratan baru bahwa catatan tidak boleh digabungkan dengan yang lama, tetapi sebaliknya, meskipun ada bidang nol, mereka digunakan untuk menimpa yang lama:
public User update(Long id, User request) {
   return userDAO.update(user);
}
Ini berarti bahwa mergeUser tidak lagi digunakan, tetapi akan sangat disayangkan untuk menghapusnya — bagaimana jika metode ini (atau ide dari metode ini) mungkin berguna suatu hari nanti? Kode semacam itu hanya memperumit sistem dan menimbulkan kebingungan, yang pada dasarnya tidak memiliki nilai praktis. Kita tidak boleh lupa bahwa kode dengan "dead piece" seperti itu akan sulit untuk diteruskan ke rekan kerja ketika Anda pergi ke proyek lain. Cara terbaik untuk menangani jangkar kapal adalah dengan memfaktor ulang kode, yaitu menghapus bagian kode (memilukan, saya tahu). Selain itu, saat menyiapkan jadwal pengembangan, perlu memperhitungkan jangkar tersebut (untuk mengalokasikan waktu untuk merapikan).

7. Obyek tangki septik

Untuk mendeskripsikan anti-pola ini, pertama-tama Anda harus mengenal pola kumpulan objek . Kumpulan objek (kolam sumber daya) adalah pola desain kreasional , sekumpulan objek yang diinisialisasi dan siap digunakan. Saat aplikasi membutuhkan objek, objek tersebut diambil dari kumpulan ini alih-alih dibuat ulang. Ketika suatu objek tidak lagi dibutuhkan, itu tidak dihancurkan. Sebaliknya, itu dikembalikan ke kolam. Pola ini biasanya digunakan untuk objek berat yang membutuhkan waktu lama untuk dibuat setiap kali dibutuhkan, seperti saat menghubungkan ke database. Mari kita lihat contoh kecil dan sederhana. Berikut adalah kelas yang mewakili pola ini:
class ReusablePool {
   private static ReusablePool pool;
   private List<Resource> list = new LinkedList<>();
   private ReusablePool() {
       for (int i = 0; i < 3; i++)
           list.add(new Resource());
   }
   public static ReusablePool getInstance() {
       if (pool == null) {
           pool = new ReusablePool();
       }
       return pool;
   }
   public Resource acquireResource() {
       if (list.size() == 0) {
           return new Resource();
       } else {
           Resource r = list.get(0);
           list.remove(r);
           return r;
       }
   }
   public void releaseResource(Resource r) {
       list.add(r);
   }
}
Kelas ini disajikan dalam bentuk pola/anti-pola singleton di atas , yaitu hanya ada satu objek dari jenis ini. Ini menggunakan Resourceobjek tertentu. Secara default, konstruktor mengisi kumpulan dengan 4 instance. Saat Anda mendapatkan objek, objek tersebut dihapus dari kumpulan (jika tidak ada objek yang tersedia, objek dibuat dan segera dikembalikan). Dan pada akhirnya, kami memiliki metode untuk mengembalikan objek tersebut. Objek sumber daya terlihat seperti ini:
public class Resource {
   private Map<String, String> patterns;
   public Resource() {
       patterns = new HashMap<>();
       patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
       patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
       patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
       patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   }
   public Map<String, String> getPatterns() {
       return patterns;
   }
   public void setPatterns(Map<String, String> patterns) {
       this.patterns = patterns;
   }
}
Di sini kita memiliki objek kecil yang berisi peta dengan nama pola desain sebagai kunci dan tautan Wikipedia terkait sebagai nilainya, serta metode untuk mengakses peta. Mari kita lihat utama:
class SomeMain {
   public static void main(String[] args) {
       ReusablePool pool = ReusablePool.getInstance();

       Resource firstResource = pool.acquireResource();
       Map<String, String> firstPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(firstResource);

       Resource secondResource = pool.acquireResource();
       Map<String, String> secondPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(secondResource);

       Resource thirdResource = pool.acquireResource();
       Map<String, String> thirdPatterns = firstResource.getPatterns();
       // use our map somehow...
       pool.releaseResource(thirdResource);
   }
}
Semuanya di sini cukup jelas: kita mendapatkan objek kumpulan, mendapatkan objek dengan sumber daya dari kumpulan, mendapatkan peta dari objek Sumber Daya, melakukan sesuatu dengannya, dan meletakkan semua ini pada tempatnya di kumpulan untuk digunakan kembali lebih lanjut. Voila, ini adalah pola desain kolam objek. Tapi kita berbicara tentang anti-pola, kan? Mari pertimbangkan kasus berikut dalam metode utama:
Resource fourthResource = pool.acquireResource();
   Map<String, String> fourthPatterns = firstResource.getPatterns();
// use our map somehow...
fourthPatterns.clear();
firstPatterns.put("first","blablabla");
firstPatterns.put("second","blablabla");
firstPatterns.put("third","blablabla");
firstPatterns.put("fourth","blablabla");
pool.releaseResource(fourthResource);
Di sini, sekali lagi, kita mendapatkan objek Resource, kita mendapatkan peta polanya, dan kita melakukan sesuatu dengan peta tersebut. Tapi sebelum menyimpan peta kembali ke kumpulan objek, itu dibersihkan dan kemudian diisi dengan data yang rusak, membuat objek Resource tidak cocok untuk digunakan kembali. Salah satu detail utama dari kumpulan objek adalah ketika objek dikembalikan, objek tersebut harus dikembalikan ke keadaan yang sesuai untuk digunakan kembali lebih lanjut. Jika objek yang dikembalikan ke pool tetap dalam keadaan salah atau tidak terdefinisi, maka desain kita disebut object cesspool. Apakah masuk akal untuk menyimpan objek yang tidak cocok untuk digunakan kembali? Dalam situasi ini, kita dapat membuat peta internal tidak dapat diubah di konstruktor:
public Resource() {
   patterns = new HashMap<>();
   patterns.put("proxy", "https://en.wikipedia.org/wiki/Proxy_pattern");
   patterns.put("bridge", "https://en.wikipedia.org/wiki/Bridge_pattern");
   patterns.put("facade", "https://en.wikipedia.org/wiki/Facade_pattern");
   patterns.put("builder", "https://en.wikipedia.org/wiki/Builder_pattern");
   patterns = Collections.unmodifiableMap(patterns);
}
Upaya dan keinginan untuk mengubah konten peta akan memudar berkat UnsupportedOperationException yang dihasilkannya. Anti-pola adalah jebakan yang sering dihadapi pengembang karena kurangnya waktu, kecerobohan, kurangnya pengalaman, atau tekanan dari manajer proyek. Terburu-buru, yang biasa terjadi, dapat menyebabkan masalah besar untuk aplikasi di masa mendatang, jadi Anda perlu mengetahui tentang kesalahan ini dan menghindarinya terlebih dahulu. Ini menyimpulkan bagian pertama artikel. Bersambung...
Komentar
  • Populer
  • Baru
  • Lama
Anda harus login untuk memberikan komentar
Halaman ini belum memiliki komentar