JavaRush /Blog Java /Random-MS /Apakah antipattern? Mari lihat contoh (bahagian 1)

Apakah antipattern? Mari lihat contoh (bahagian 1)

Diterbitkan dalam kumpulan
Apakah antipattern?  Mari lihat contoh (bahagian 1) - 1hari yang baik untuk semua! Pada hari yang lain saya telah ditemu bual, dan saya ditanya soalan tentang antipattern: apakah jenis binatang ini, apakah jenis dan contoh mereka dalam amalan. Sudah tentu, saya menjawab soalan itu, tetapi sangat dangkal, kerana saya tidak mendalami kajian isu ini. Selepas temu bual, saya mula menjelajah Internet, menjadi lebih dan lebih tenggelam dalam topik ini. Hari ini saya ingin membuat ulasan ringkas tentang antipattern yang paling popular dan contoh mereka, membaca yang mungkin memberi anda pengetahuan yang diperlukan mengenai isu ini. Mari kita mulakan! Jadi, sebelum membincangkan apa itu antipattern, mari kita ingat apa itu corak. Corak ialah reka bentuk seni bina yang boleh diulang untuk menyelesaikan masalah atau situasi biasa yang timbul semasa mereka bentuk aplikasi. Tetapi hari ini kita tidak bercakap tentang mereka, tetapi tentang lawan mereka - antipatterns. Anti-corak ialah pendekatan biasa untuk menyelesaikan kelas masalah yang biasa dihadapi yang tidak berkesan, berisiko atau tidak produktif. Dalam erti kata lain, ia adalah corak ralat (juga kadang-kadang dipanggil perangkap). Apakah antipattern?  Mari lihat contoh (bahagian 1) - 2Sebagai peraturan, antipattern dibahagikan kepada jenis berikut:
  1. Antipattern seni bina - antipattern seni bina yang timbul semasa mereka bentuk struktur sistem (biasanya oleh arkitek).
  2. Management Anti Pattern - antipattern dalam bidang pengurusan, yang biasanya ditemui oleh pelbagai pengurus (atau kumpulan pengurus).
  3. Development Anti Pattern - antipattern adalah masalah pembangunan yang timbul apabila pengaturcara biasa menulis sistem.
Eksotisme antipattern jauh lebih luas, tetapi kami tidak akan menganggapnya hari ini, kerana bagi pemaju biasa ini akan menjadi luar biasa. Sebagai permulaan, mari kita ambil contoh antipattern dalam bidang pengurusan.

1. Lumpuh analisis

Kelumpuhan analisis dianggap sebagai anti-corak organisasi klasik. Ia melibatkan terlalu banyak menganalisis situasi semasa merancang supaya tiada keputusan atau tindakan diambil, pada dasarnya melumpuhkan pembangunan. Ini sering berlaku apabila matlamatnya adalah untuk mencapai kesempurnaan dan penyelesaian lengkap tempoh analisis. Anti-corak ini dicirikan dengan berjalan dalam bulatan (sejenis gelung tertutup), menyemak dan mencipta model terperinci, yang seterusnya mengganggu aliran kerja. Sebagai contoh, anda cuba meramalkan perkara seperti: bagaimana jika pengguna tiba-tiba ingin membuat senarai pekerja berdasarkan huruf keempat dan kelima nama mereka, termasuk projek yang mereka menumpukan paling banyak waktu bekerja antara Tahun Baru dan lapan Mac dalam empat tahun sebelumnya ? Pada dasarnya, ini adalah analisis yang berlebihan. Contoh kehidupan sebenar yang baik ialah bagaimana kelumpuhan analisis menyebabkan Kodak menjadi muflis . Berikut ialah beberapa petua kecil untuk memerangi lumpuh analisis:
  1. Anda perlu menentukan matlamat jangka panjang sebagai suar untuk membuat keputusan, supaya setiap keputusan yang anda buat membawa anda lebih dekat kepada matlamat anda, dan tidak memaksa anda untuk menandakan masa.
  2. Jangan menumpukan perhatian pada perkara kecil (mengapa membuat keputusan pada nuansa kecil seolah-olah ia adalah yang terakhir dalam hidup anda?)
  3. Tetapkan tarikh akhir untuk membuat keputusan.
  4. Jangan cuba melakukan tugas dengan sempurna: lebih baik melakukannya dengan baik.
Kami tidak akan pergi terlalu mendalam dan mempertimbangkan anticorak pengurusan lain sekarang. Oleh itu, tanpa mukadimah, mari kita beralih kepada beberapa anticorak seni bina, kerana kemungkinan besar, artikel ini dibaca oleh pembangun masa depan, bukan pengurus.

2. objek Tuhan

Objek ilahi ialah anti-corak yang menerangkan kepekatan berlebihan bagi terlalu banyak fungsi yang berbeza, menyimpan sejumlah besar data yang pelbagai (objek di sekeliling 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 beberapa jenis kelas besar yang melakukan segala-galanya sekaligus. Mengandungi pertanyaan kepada Pangkalan Data, mengandungi beberapa data, kami juga melihat kaedah fasad findAllWithoutPageEndengan logik perniagaan. Objek ketuhanan sedemikian menjadi besar dan kekok untuk disokong secukupnya. Kita perlu memikirkannya dalam setiap bahagian kod: banyak nod dalam sistem bergantung padanya dan digandingkan dengannya. Mengekalkan kod sedemikian menjadi semakin sukar. Dalam kes sedemikian, ia perlu dibahagikan kepada kelas yang berasingan, setiap satu daripadanya hanya mempunyai satu tujuan (matlamat). Dalam contoh ini, kita boleh memecahkannya kepada 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 mengandungi data dan kaedah 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 adalah lebih sesuai untuk memindahkan kaedah dengan logik perniagaan ke dalam perkhidmatan:
private List<User> findAllWithoutPageEn(Long permissionId, Type type) {
   switch (type) {
       case USERS:
           return findAllEnUsers(permissionId);
       case CUSTOMERS:
           return findAllEnCustomers(permissionId);
       default:
           return findAllEn();
   }
}

3. Bujang

Singleton ialah corak paling mudah yang menjamin bahawa akan terdapat satu contoh bagi sesetengah kelas dalam aplikasi berbenang tunggal dan menyediakan pusat akses global kepada objek itu. Anda boleh membaca lebih lanjut mengenainya di sini . Tetapi adakah ini corak atau antipattern? Apakah antipattern?  Mari lihat contoh (bahagian 1) - 3Mari lihat keburukan templat ini:
  1. keadaan global. Apabila kita mengakses tika kelas, kita tidak tahu keadaan semasa kelas itu atau siapa yang mengubahnya atau bila, dan keadaan itu mungkin bukan seperti yang kita harapkan. Dalam erti kata lain, ketepatan bekerja dengan singleton bergantung pada susunan panggilan kepadanya, yang menyebabkan subsistem bergantung antara satu sama lain dan, akibatnya, secara serius meningkatkan kerumitan pembangunan.

  2. Singleton melanggar salah satu prinsip SOLID - Prinsip Tanggungjawab Tunggal - kelas Singleton, selain melaksanakan tanggungjawab segeranya, juga mengawal bilangan kejadiannya.

  3. Kebergantungan kelas biasa pada singleton tidak kelihatan dalam antara muka kelas. Oleh kerana biasanya contoh tunggal tidak diluluskan dalam parameter kaedah, tetapi diperoleh secara langsung, melalui getInstance(), untuk mengenal pasti pergantungan kelas pada tunggal, anda perlu menyelidiki pelaksanaan setiap kaedah - hanya melihat kontrak awam objek tidak mencukupi .

    Kehadiran singleton mengurangkan kebolehujian aplikasi secara umum dan kelas yang menggunakan singleton khususnya. Pertama, anda tidak boleh meletakkan objek Olok-olok sebagai ganti singleton, dan kedua, jika singleton mempunyai antara muka untuk menukar keadaannya, ujian akan bergantung antara satu sama lain.

    Dalam erti kata lain, singleton meningkatkan ketersambungan, dan semua perkara di atas tidak lebih daripada akibat peningkatan ketersambungan.

    Dan jika anda memikirkannya, menggunakan singleton boleh dielakkan. Sebagai contoh, untuk mengawal bilangan kejadian sesuatu objek, adalah mungkin (dan perlu) untuk menggunakan pelbagai jenis kilang.

    Bahaya terbesar terletak pada percubaan untuk membina keseluruhan seni bina aplikasi berdasarkan singleton. Terdapat banyak alternatif yang hebat untuk pendekatan ini. Contoh paling penting ialah Spring, iaitu bekas IoCnya: di sana masalah mengawal penciptaan perkhidmatan diselesaikan secara semula jadi, kerana mereka, sebenarnya, adalah "kilang steroid".

    Kini terdapat banyak holivar tentang topik ini, jadi terpulang kepada anda untuk memutuskan sama ada singleton ialah corak atau antipattern.

    Dan kami tidak akan memikirkannya dan beralih kepada corak reka bentuk terakhir untuk hari ini - poltergeist.

4. Poltergeist

Poltergeist ialah antipattern kelas tidak berguna yang digunakan untuk memanggil kaedah kelas lain atau hanya menambah lapisan abstraksi yang tidak perlu. Antipattern menampakkan dirinya dalam bentuk objek jangka pendek tanpa keadaan. Objek ini sering digunakan untuk memulakan 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);
   }
}
Mengapakah kita memerlukan objek yang hanya perantara dan mewakilkan kerjanya kepada orang lain? Kami memadamkannya dan mengalihkan fungsi kecil yang dilaksanakannya ke dalam objek tahan lama. Seterusnya, kami beralih kepada corak yang paling menarik minat kami (sebagai pemaju biasa) - antipattern pembangunan .

5.Kod keras

Jadi kita sampai kepada perkataan yang mengerikan ini - hardcode. Intipati antipattern ini ialah kod itu terikat kuat pada konfigurasi perkakasan dan/atau persekitaran sistem tertentu, yang menjadikannya sangat sukar untuk mengalihkannya ke konfigurasi lain. Antipattern ini berkait rapat dengan nombor ajaib (ia sering saling berkait). 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 kami terus menetapkan konfigurasi sambungan kami; akibatnya, kod akan berfungsi dengan betul hanya dengan MySQL, dan untuk menukar pangkalan data anda perlu masuk ke dalam kod dan menukar semuanya secara manual. Penyelesaian yang baik ialah meletakkan konfigurasi dalam fail berasingan:
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 ialah memindahkannya ke pemalar.

6. Sauh bot

Sauh bot dalam konteks antipattern bermaksud menyimpan bahagian sistem yang tidak digunakan yang tertinggal selepas beberapa pengoptimuman atau pemfaktoran semula. Selain itu, beberapa bahagian kod boleh ditinggalkan "untuk masa hadapan", sekiranya anda perlu menggunakannya semula. Ini pada asasnya menukar kod menjadi tong sampah. Apakah antipattern?  Mari lihat contoh (bahagian 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 mempunyai kaedah kemas kini yang menggunakan kaedah berasingan untuk menggabungkan data pengguna daripada pangkalan data dan orang yang datang untuk kemas kini (jika orang yang datang untuk kemas kini mempunyai medan kosong, maka ia ditulis sebagai yang lama daripada pangkalan data). Dan sebagai contoh, terdapat keperluan bahawa rekod tidak boleh digabungkan dengan yang lama, tetapi ditimpa, walaupun terdapat medan kosong:
public User update(Long id, User request) {
   return userDAO.update(user);
}
Akibatnya, mergeUseria tidak lagi digunakan dan sayang untuk memadamkannya: bagaimana jika ia (atau ideanya) masih berguna? Kod sedemikian hanya merumitkan dan mengelirukan sistem, pada dasarnya tidak memberikan sebarang nilai praktikal sama sekali. Kita tidak boleh lupa bahawa kod seperti "mati" akan sukar untuk dipindahkan kepada rakan sekerja apabila anda pergi untuk projek lain. Kaedah terbaik untuk menangani sauh bot ialah pemfaktoran semula kod, iaitu, memadam bahagian kod ini (alas, sayangnya). Juga, apabila merancang pembangunan, anda perlu mengambil kira kejadian sauh tersebut (berikan masa untuk membersihkan tailing).

7.Kolam kumbahan objek

Untuk menerangkan antipattern ini, anda perlu membiasakan diri dengan corak kumpulan objek . Kolam objek (kolam sumber) ialah corak reka bentuk generatif , set objek yang dimulakan dan sedia untuk digunakan. Apabila aplikasi memerlukan objek, ia tidak dicipta semula, tetapi diambil dari kolam ini. Apabila objek tidak lagi diperlukan, ia tidak dimusnahkan, tetapi dikembalikan ke kolam. Biasanya digunakan untuk objek berat yang intensif sumber untuk dibuat setiap kali, seperti sambungan pangkalan data. Mari kita lihat contoh yang kecil dan mudah untuk dijadikan contoh. Jadi kami mempunyai kelas yang mewakili corak 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 membentangkan kelas ini dalam bentuk singleton pattern/antipattern yang diterangkan di atas , iaitu, terdapat hanya satu objek jenis ini, ia beroperasi pada objek tertentu Resource, secara lalai dalam pembina kolam diisi dengan 4 salinan; apabila objek sedemikian diambil, ia dikeluarkan dari kolam (jika ia tidak ada, ia dicipta dan segera diberikan), dan pada akhirnya terdapat kaedah untuk meletakkan objek itu kembali. Objek Resourcekelihatan 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 mempunyai objek kecil yang mengandungi peta dengan nama corak sebagai kunci dan pautan kepada mereka sebagai nilai, serta kaedah 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);
   }
}
Segala-galanya di sini juga jelas: kami mengambil objek kolam, mengeluarkan objek dengan sumber daripadanya, mengambil peta daripadanya, melakukan sesuatu dengannya dan meletakkannya semula ke dalam kolam untuk kegunaan semula selanjutnya. Voila: di sini anda mempunyai corak kolam objek. Tetapi kita bercakap tentang antipattern, bukan? Mari kita lihat kes 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 diambil, petanya dengan corak diambil dan sesuatu dilakukan dengannya, tetapi sebelum menyimpan semula ke kolam objek, peta dibersihkan dan diisi dengan data yang tidak dapat difahami yang menjadikan objek Sumber ini tidak sesuai untuk digunakan semula. Salah satu nuansa utama kumpulan objek ialah selepas objek dikembalikan, ia mesti dikembalikan ke keadaan yang sesuai untuk digunakan semula selanjutnya. Jika objek berada dalam keadaan tidak betul atau tidak ditentukan selepas dikembalikan ke kolam, binaan ini dipanggil kolam kumbahan objek. Adakah masuk akal untuk kita menyimpan objek yang tidak sesuai untuk digunakan semula? Dalam keadaan ini, anda boleh membuat peta dalaman tidak berubah dalam pembina:
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);
}
(percubaan dan keinginan untuk menukar kandungan akan jatuh bersama dengan UnsupportedOperationException). Antipattern ialah perangkap yang sering ditimbulkan oleh pembangun kerana kekurangan masa, kurang perhatian, kurang pengalaman atau sepakan daripada pengurus. Kekurangan masa dan tergesa-gesa yang biasa boleh mengakibatkan masalah besar untuk aplikasi pada masa hadapan, jadi kesilapan ini perlu diketahui dan dielakkan terlebih dahulu. Apakah antipattern?  Mari lihat contoh (bahagian 1) - 6Dengan ini, bahagian pertama artikel telah berakhir: akan diteruskan .
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION