JavaRush /Java Blog /Random-ID /Apa itu antipola? Mari kita lihat contohnya (bagian 1)

Apa itu antipola? Mari kita lihat contohnya (bagian 1)

Dipublikasikan di grup Random-ID
Apa itu antipola?  Mari kita lihat contoh (bagian 1) - 1Hari baik semuanya! Suatu hari saya diwawancarai dan ditanyai pertanyaan tentang antipola: binatang macam apa ini, apa jenisnya dan contoh praktiknya. Tentu saja, saya menjawab pertanyaan itu, tetapi dengan sangat dangkal, karena saya tidak mempelajari masalah ini secara mendalam. Setelah wawancara, saya mulai menjelajahi Internet, semakin tenggelam dalam topik ini. Hari ini saya ingin membuat ulasan singkat tentang antipola paling populer dan contohnya, bacaan yang mungkin memberi Anda pengetahuan yang diperlukan tentang masalah ini. Mari kita mulai! Jadi, sebelum membahas apa itu antipattern, mari kita ingat dulu apa itu pola. Pola adalah desain arsitektur yang dapat diulang untuk memecahkan masalah atau situasi umum yang muncul saat merancang sebuah aplikasi. Namun hari ini kita tidak membicarakannya, tetapi tentang kebalikannya - antipola. Anti-pola adalah pendekatan umum untuk memecahkan serangkaian masalah umum yang tidak efektif, berisiko, atau tidak produktif. Dengan kata lain, ini adalah pola kesalahan (terkadang juga disebut jebakan). Apa itu antipola?  Mari kita lihat contoh (bagian 1) - 2Biasanya, antipola dibagi menjadi beberapa jenis berikut:
  1. Antipola arsitektur - antipola arsitektur yang muncul ketika merancang struktur suatu sistem (biasanya oleh seorang arsitek).
  2. Anti Pola Manajemen - antipola dalam bidang manajemen, yang biasanya ditemui oleh berbagai manajer (atau kelompok manajer).
  3. Anti Pola Pengembangan - antipola adalah masalah pengembangan yang muncul ketika pemrogram biasa menulis suatu sistem.
Eksotisme antipola jauh lebih luas, tetapi kami tidak akan mempertimbangkannya hari ini, karena bagi pengembang biasa hal ini akan sangat membebani. Untuk memulainya, mari kita ambil contoh antipola dalam bidang manajemen.

1. Kelumpuhan analitis

Kelumpuhan analisis dianggap sebagai anti-pola organisasi klasik. Hal ini melibatkan analisis situasi yang berlebihan ketika membuat perencanaan sehingga tidak ada keputusan atau tindakan yang diambil, yang pada dasarnya melumpuhkan pembangunan. Hal ini sering terjadi ketika tujuannya adalah untuk mencapai kesempurnaan dan penyelesaian periode analisis secara tuntas. Anti-pola ini ditandai dengan berjalan berputar-putar (semacam putaran tertutup), merevisi dan membuat model detail, yang pada akhirnya mengganggu alur kerja. Misalnya, Anda mencoba memprediksi hal-hal seperti: bagaimana jika pengguna tiba-tiba ingin membuat daftar karyawan berdasarkan huruf keempat dan kelima dari namanya, termasuk dalam daftar proyek yang paling banyak mereka curahkan jam kerjanya di antara karyawan tersebut. Tahun Baru dan Tanggal Delapan Maret pada empat tahun sebelumnya ? Intinya, ini adalah analisis yang berlebihan. Contoh nyata yang bagus adalah bagaimana kelumpuhan analisis menyebabkan Kodak bangkrut . Berikut adalah beberapa tip kecil untuk memerangi kelumpuhan analisis:
  1. Anda perlu mendefinisikan tujuan jangka panjang sebagai mercusuar pengambilan keputusan, sehingga setiap keputusan yang Anda ambil membawa Anda lebih dekat ke tujuan Anda, dan tidak memaksa Anda untuk menandai waktu.
  2. Jangan berkonsentrasi pada hal-hal sepele (mengapa mengambil keputusan pada hal kecil seolah-olah itu adalah keputusan terakhir dalam hidup Anda?)
  3. Tetapkan tenggat waktu untuk mengambil keputusan.
  4. Jangan mencoba melakukan suatu tugas dengan sempurna: lebih baik melakukannya dengan sangat baik.
Kami tidak akan membahas terlalu dalam dan mempertimbangkan antipola manajemen lainnya sekarang. Oleh karena itu, tanpa basa-basi, mari kita beralih ke beberapa antipola arsitektural, karena kemungkinan besar, artikel ini dibaca oleh pengembang masa depan, bukan manajer.

2.Tuhan berkeberatan

Objek ilahi adalah anti-pola yang menggambarkan konsentrasi berlebihan dari terlalu banyak fungsi yang berbeda, menyimpan sejumlah besar data yang beragam (objek di mana aplikasi berputar). Mari kita 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 semacam kelas besar yang melakukan semuanya sekaligus. Berisi query ke Database, berisi beberapa data, kami juga melihat metode fasad findAllWithoutPageEndengan logika bisnis. Objek ilahi seperti itu menjadi besar dan tidak praktis untuk ditopang secara memadai. Kita harus mengotak-atiknya di setiap bagian kode: banyak node di sistem bergantung padanya dan terhubung erat dengannya. Mempertahankan kode tersebut menjadi semakin sulit. Dalam kasus seperti itu, perlu dibagi menjadi kelas-kelas terpisah, yang masing-masing hanya memiliki satu tujuan (goal). Dalam contoh ini, kita dapat memecahnya 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 mengaksesnya:
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 dalam 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 paling sederhana yang menjamin bahwa akan ada satu instance dari beberapa kelas dalam aplikasi single-thread dan menyediakan jalur akses global ke objek tersebut. Anda dapat membaca lebih lanjut tentangnya di sini . Tapi apakah ini sebuah pola atau antipattern? Apa itu antipola?  Mari kita lihat contoh (bagian 1) - 3Mari kita lihat kekurangan dari template ini:
  1. negara global. Saat kita mengakses sebuah instance dari suatu kelas, kita tidak mengetahui status kelas tersebut saat ini atau siapa yang mengubahnya atau kapan, dan status tersebut mungkin tidak seperti yang kita harapkan. Dengan kata lain, kebenaran bekerja dengan singleton bergantung pada urutan pemanggilannya, yang menyebabkan subsistem bergantung satu sama lain dan, sebagai akibatnya, sangat meningkatkan kompleksitas pengembangan.

  2. Singleton melanggar salah satu prinsip SOLID - Prinsip Tanggung Jawab Tunggal - kelas Singleton, selain menjalankan tanggung jawab langsungnya, juga mengontrol jumlah instance-nya.

  3. Ketergantungan kelas reguler pada singleton tidak terlihat di antarmuka kelas. Karena biasanya sebuah instance dari singleton tidak diteruskan dalam parameter suatu metode, tetapi diperoleh secara langsung, melalui getInstance(), untuk mengidentifikasi ketergantungan suatu kelas pada singleton, Anda perlu mempelajari implementasi setiap metode - cukup dengan melihat publik kontrak objek saja tidak cukup.

    Kehadiran singleton mengurangi kemampuan pengujian aplikasi secara umum dan kelas yang menggunakan singleton pada khususnya. Pertama, Anda tidak dapat menempatkan objek Mock sebagai pengganti singleton, dan kedua, jika singleton memiliki antarmuka untuk mengubah statusnya, pengujian akan bergantung satu sama lain.

    Dengan kata lain, singleton meningkatkan konektivitas, dan semua hal di atas tidak lebih dari konsekuensi peningkatan konektivitas.

    Dan jika dipikir-pikir, penggunaan singleton bisa dihindari. Misalnya, untuk mengontrol jumlah instance suatu objek, sangat mungkin (dan perlu) menggunakan berbagai macam pabrik.

    Bahaya terbesar terletak pada upaya membangun seluruh arsitektur aplikasi berdasarkan singleton. Ada banyak alternatif bagus untuk pendekatan ini. Contoh yang paling penting adalah Spring, yaitu wadah IoC-nya: di sana masalah pengendalian penciptaan layanan diselesaikan secara alami, karena pada kenyataannya, mereka adalah “pabrik steroid”.

    Sekarang ada banyak holivar tentang topik ini, jadi terserah Anda untuk memutuskan apakah singleton adalah sebuah pola atau antipattern.

    Dan kami tidak akan memikirkannya dan beralih ke pola desain terakhir hari ini - poltergeist.

4. poltergeist

Poltergeist adalah antipola kelas yang tidak berguna yang digunakan untuk memanggil metode kelas lain atau sekadar menambahkan lapisan abstraksi yang tidak perlu. Antipola memanifestasikan dirinya dalam bentuk objek berumur pendek tanpa keadaan. Objek ini sering digunakan untuk menginisialisasi objek lain yang lebih tahan lama.
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 suatu benda yang hanya sekedar perantara dan mendelegasikan pekerjaannya kepada orang lain? Kami menghapusnya, dan memindahkan fungsionalitas kecil yang diimplementasikannya ke dalam objek yang berumur panjang. Selanjutnya, kita beralih ke pola yang paling menarik bagi kami (sebagai pengembang biasa) - antipola pengembangan .

5. Kode keras

Jadi kita sampai pada kata buruk ini - hardcode. Inti dari antipola ini adalah bahwa kode tersebut sangat terkait dengan konfigurasi perangkat keras dan/atau lingkungan sistem tertentu, sehingga sangat sulit untuk mem-portingnya ke konfigurasi lain. Antipola ini terkait erat dengan angka ajaib (seringkali saling terkait). 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;
}
Dipaku, bukan? Di sini kita langsung mengatur konfigurasi koneksi kita; sebagai hasilnya, kode hanya akan berfungsi dengan baik dengan MySQL, dan untuk mengubah database Anda perlu masuk ke 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 lainnya adalah memindahkannya ke konstanta.

6. Jangkar perahu

Jangkar perahu dalam konteks antipola berarti menyimpan bagian sistem yang tidak terpakai yang tersisa setelah beberapa optimasi atau pemfaktoran ulang. Selain itu, beberapa bagian kode mungkin dibiarkan “untuk masa depan”, jika Anda harus menggunakannya lagi. Ini pada dasarnya mengubah kode menjadi tempat sampah. Apa itu antipola?  Mari kita lihat contoh (bagian 1) - 4Contoh:
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 dan orang yang datang untuk pembaruan (jika orang yang datang untuk pembaruan memiliki bidang kosong, maka ditulis sebagai yang lama) dari basis data). Dan misalnya, ada persyaratan bahwa catatan tidak boleh digabungkan dengan yang lama, tetapi ditimpa, meskipun ada bidang kosong:
public User update(Long id, User request) {
   return userDAO.update(user);
}
Alhasil mergeUsersudah tidak terpakai lagi dan sayang untuk dihapus: bagaimana jika (atau idenya) masih berguna? Kode seperti itu hanya memperumit dan membingungkan sistem, pada dasarnya tidak memberikan nilai praktis sama sekali. Kita tidak boleh lupa bahwa kode dengan "potongan mati" seperti itu akan sulit untuk ditransfer ke kolega ketika Anda berangkat ke proyek lain. Metode terbaik untuk menangani jangkar kapal adalah pemfaktoran ulang kode, yaitu menghapus bagian kode ini (sayangnya, sayang sekali). Selain itu, ketika merencanakan pengembangan, Anda perlu memperhitungkan kemunculan jangkar tersebut (berikan waktu untuk membersihkan tailing).

7.Objek tangki septik

Untuk mendeskripsikan antipattern ini, pertama-tama Anda perlu mengenal pola kumpulan objek . Kumpulan objek (resource pool) adalah pola desain generatif , sekumpulan objek yang diinisialisasi dan siap digunakan. Ketika suatu aplikasi memerlukan suatu objek, objek tersebut tidak dibuat lagi, tetapi diambil dari kumpulan ini. Ketika suatu benda tidak diperlukan lagi, benda tersebut tidak dimusnahkan, tetapi dikembalikan ke kolam. Biasanya digunakan untuk objek berat yang membutuhkan banyak sumber daya untuk dibuat setiap saat, seperti koneksi database. Mari kita lihat contoh kecil dan sederhana demi contoh. Jadi kita memiliki 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);
   }
}
Kami menyajikan kelas ini dalam bentuk pola/antipattern tunggal yang dijelaskan di atas , yaitu, hanya ada satu objek jenis ini, ia beroperasi pada objek tertentu Resource, secara default di konstruktor kumpulan diisi dengan 4 salinan; ketika benda tersebut diambil, maka dikeluarkan dari kolam (jika tidak ada, dibuat dan segera diberikan), dan pada akhirnya ada metode untuk mengembalikan benda tersebut. Objeknya Resourceterlihat seperti ini:
public class Resource {
   private Map<String, String> patterns;
   public Resource() {
       patterns = new HashMap<>();
       patterns.put("заместитель", "https://studfile.net/preview/3676297/page:3/");
       patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
       patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
       patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
   }
   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 sebagai kunci dan tautan ke pola tersebut sebagai nilainya, serta metode untuk mengakses peta. Mari lihat main:
class SomeMain {
   public static void main(String[] args) {
       ReusablePool pool = ReusablePool.getInstance();

       Resource firstResource = pool.acquireResource();
       Map<String, String> firstPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(firstResource);

       Resource secondResource = pool.acquireResource();
       Map<String, String> secondPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(secondResource);

       Resource thirdResource = pool.acquireResource();
       Map<String, String> thirdPatterns = firstResource.getPatterns();
       // ......Howим-то образом используем нашу мапу.....
       pool.releaseResource(thirdResource);
   }
}
Semuanya di sini juga jelas: kita mengambil objek kumpulan, mengeluarkan objek dengan sumber daya darinya, mengambil peta darinya, melakukan sesuatu dengannya, dan mengembalikan semuanya ke dalam kumpulan untuk digunakan kembali lebih lanjut. Voila: di sini Anda memiliki pola kumpulan objek. Tapi kita sedang membicarakan antipola, bukan? Mari kita lihat kasus ini main:
Resource fourthResource = pool.acquireResource();
   Map<String, String> fourthPatterns = firstResource.getPatterns();
// ......Howим-то образом используем нашу мапу.....
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, objek sumber daya diambil, petanya dengan pola diambil dan sesuatu dilakukan dengannya, tetapi sebelum menyimpan kembali ke kumpulan objek, peta dibersihkan dan diisi dengan data yang tidak dapat dipahami yang membuat objek Sumber Daya ini tidak cocok untuk digunakan kembali. Salah satu nuansa utama kumpulan objek adalah bahwa setelah suatu objek dikembalikan, objek tersebut harus dikembalikan ke keadaan yang sesuai untuk digunakan kembali lebih lanjut. Jika objek berada dalam keadaan yang salah atau tidak terdefinisi setelah dikembalikan ke kumpulan, konstruksi ini disebut tangki septik objek. Apa gunanya menyimpan benda yang tidak bisa digunakan kembali? Dalam situasi ini, Anda dapat membuat peta internal tidak dapat diubah di konstruktor:
public Resource() {
   patterns = new HashMap<>();
   patterns.put("заместитель", "https://studfile.net/preview/3676297/page:3/");
   patterns.put("мост", "https://studfile.net/preview/3676297/page:4/");
   patterns.put("фасад", "https://studfile.net/preview/3676297/page:5/");
   patterns.put("строитель", "https://studfile.net/preview/3676297/page:6/#16");
   patterns = Collections.unmodifiableMap(patterns);
}
(usaha dan keinginan untuk mengubah konten akan gagal bersamaan dengan UnsupportedOperationException). Antipola adalah jebakan yang sering dialami pengembang karena kurangnya waktu, kurangnya perhatian, kurangnya pengalaman, atau penolakan dari manajer. Kurangnya waktu dan ketergesaan yang biasa terjadi dapat mengakibatkan masalah besar pada aplikasi di kemudian hari, sehingga kesalahan tersebut perlu diketahui dan dihindari terlebih dahulu. Apa itu antipola?  Mari kita lihat contoh (bagian 1) - 6Dengan ini, bagian pertama artikel telah berakhir: dilanjutkan .
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION