CodeGym/Blog Java/rawak/Apakah anti-corak? Mari lihat beberapa contoh (Bahagian 1...
John Squirrels
Tahap
San Francisco

Apakah anti-corak? Mari lihat beberapa contoh (Bahagian 1)

Diterbitkan dalam kumpulan
hari yang baik untuk semua! Pada hari yang lain saya mengadakan temu duga kerja, dan saya ditanya beberapa soalan tentang anti-corak: apakah itu, jenis apa yang ada, dan apakah contoh praktikal yang ada. Sudah tentu, saya menjawab soalan itu, tetapi sangat cetek, kerana saya sebelum ini tidak menyelami topik ini. Selepas temu bual, saya mula melayari Internet dan semakin mendalami topik tersebut. Apakah anti-corak?  Mari lihat beberapa contoh (Bahagian 1) - 1 Hari ini saya ingin memberikan gambaran ringkas tentang anti-corak yang paling popular dan mengkaji beberapa contoh. Saya harap membaca ini akan memberi anda pengetahuan yang anda perlukan dalam bidang ini. Mari kita mulakan! Sebelum kita membincangkan apa itu anti-corak, mari kita ingat semula apa itu corak reka bentuk. Corak reka bentukialah penyelesaian seni bina berulang untuk masalah atau situasi biasa yang timbul semasa mereka bentuk aplikasi. Tetapi hari ini kita tidak bercakap tentang mereka, tetapi sebaliknya - anti-corak. Anti -corak ialah pendekatan yang meluas tetapi tidak berkesan, berisiko dan/atau tidak produktif untuk menyelesaikan kelas masalah biasa. Dalam erti kata lain, ini adalah corak kesilapan (juga kadang-kadang dipanggil perangkap). Sebagai peraturan, anti-corak dibahagikan kepada jenis berikut:
  1. Anti-corak seni bina — Anti-corak ini timbul apabila struktur sistem direka (biasanya oleh arkitek).
  2. Anti-corak pengurusan/organisasi — Ini adalah anti-corak dalam pengurusan projek, biasanya ditemui oleh pelbagai pengurus (atau kumpulan pengurus).
  3. Pembangunan anti-corak — Anti-corak ini timbul apabila sistem dilaksanakan oleh pengaturcara biasa.
Rangkaian penuh anti-corak jauh lebih eksotik, tetapi kami tidak akan menganggap mereka semua hari ini. Bagi pemaju biasa, itu akan menjadi terlalu banyak. Sebagai permulaan, mari kita pertimbangkan anti-corak pengurusan sebagai contoh.

1. Lumpuh analisis

Kelumpuhan analisisdianggap sebagai anti-corak pengurusan klasik. Ia melibatkan terlalu menganalisis keadaan semasa perancangan, supaya tiada keputusan atau tindakan diambil, pada dasarnya melumpuhkan proses pembangunan. Ini sering berlaku apabila matlamatnya adalah untuk mencapai kesempurnaan dan mempertimbangkan sepenuhnya segala-galanya semasa tempoh analisis. Anti-corak ini dicirikan dengan berjalan dalam bulatan (gelung tertutup run-of-the-mill), menyemak dan mencipta model terperinci, yang seterusnya mengganggu aliran kerja. Sebagai contoh, anda cuba meramalkan perkara pada satu tahap: tetapi bagaimana jika pengguna tiba-tiba ingin membuat senarai pekerja berdasarkan huruf keempat dan kelima nama mereka, termasuk senarai projek yang paling banyak menghabiskan masa bekerja mereka antara Tahun Baru dan Hari Wanita Antarabangsa sejak empat tahun lalu? Pada dasarnya, ia' s terlalu banyak analisis. Berikut adalah beberapa petua untuk memerangi lumpuh analisis:
  1. Anda perlu menentukan matlamat jangka panjang sebagai suar untuk membuat keputusan, supaya setiap keputusan anda mendekatkan anda kepada matlamat dan bukannya menyebabkan anda terbantut.
  2. Jangan menumpukan perhatian kepada perkara-perkara kecil (mengapa membuat keputusan tentang perincian yang tidak penting seolah-olah itu adalah keputusan yang paling penting dalam hidup anda?)
  3. Tetapkan tarikh akhir untuk membuat keputusan.
  4. Jangan cuba menyelesaikan tugas dengan sempurna — lebih baik melakukannya dengan baik.
Tidak perlu pergi terlalu dalam di sini, jadi kami tidak akan mempertimbangkan anti-corak pengurusan yang lain. Oleh itu, tanpa sebarang pengenalan, kami akan beralih kepada beberapa anti-corak seni bina, kerana artikel ini berkemungkinan besar akan dibaca oleh pembangun masa hadapan dan bukannya pengurus.

2. Tuhan membantah

Objek Tuhan ialah anti-corak yang menerangkan kepekatan berlebihan semua jenis fungsi dan sejumlah besar data yang berbeza (objek yang aplikasi berputar di sekeliling). 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 segala-galanya. Ia mengandungi pertanyaan pangkalan data serta beberapa data. Kami juga melihat kaedah fasad findAllWithoutPageEn, yang merangkumi logik perniagaan. Objek Tuhan sedemikian menjadi besar dan janggal untuk dijaga dengan betul. Kita perlu mengacaukannya dalam setiap bahagian kod. Banyak komponen sistem bergantung padanya dan berganding rapat dengannya. Ia menjadi lebih sukar dan lebih sukar untuk mengekalkan kod tersebut. Dalam kes sedemikian, kod harus dibahagikan kepada kelas yang berasingan, yang setiap satunya akan mempunyai satu tujuan sahaja. Dalam contoh ini, kita boleh membahagikan objek Tuhan 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 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 adalah lebih sesuai untuk mengalihkan kaedah dengan logik perniagaan kepada 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. Singleton

Singleton adalah corak yang paling mudah. Ia memastikan bahawa dalam aplikasi satu-benang akan terdapat satu contoh kelas, dan ia menyediakan pusat akses global kepada objek ini. Tetapi adakah ia corak atau anti-corak? Mari lihat keburukan corak ini:
  1. Keadaan global Apabila kita mengakses contoh kelas, kita tidak mengetahui keadaan semasa kelas ini. Kita tidak tahu siapa yang mengubahnya atau bila. Negeri mungkin tidak seperti yang kita harapkan. Dengan kata lain, ketepatan bekerja dengan singleton bergantung pada susunan akses kepadanya. Ini bermakna bahawa subsistem bergantung antara satu sama lain dan, akibatnya, reka bentuk menjadi lebih kompleks.

  2. Singleton melanggar prinsip SOLID — prinsip tanggungjawab tunggal: sebagai tambahan kepada tugas langsungnya, kelas singleton juga mengawal bilangan kejadian.

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

    Kehadiran singleton mengurangkan kebolehujian aplikasi secara keseluruhan dan kelas yang menggunakan singleton khususnya. Pertama sekali, anda tidak boleh menggantikan singleton dengan objek olok-olok. Kedua, jika singleton mempunyai antara muka untuk menukar keadaannya, maka ujian akan bergantung pada satu sama lain.

    Dalam erti kata lain, singleton meningkatkan gandingan, dan semua yang disebutkan di atas tidak lebih daripada akibat daripada peningkatan gandingan.

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

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

    Banyak perdebatan yang tidak berkesudahan dan tidak dapat didamaikan kini berkobar mengenai perkara ini. Terpulang kepada anda untuk memutuskan sama ada singleton adalah corak atau anti-corak.

    Kami tidak akan berlama-lama mengenainya. Sebaliknya, kita akan beralih kepada corak reka bentuk terakhir untuk hari ini — poltergeist.

4. Poltergeist

Poltergeist ialah anti-corak yang melibatkan kelas sia-sia yang digunakan untuk memanggil kaedah kelas lain atau hanya menambah lapisan abstraksi yang tidak perlu . Anti-corak ini menunjukkan dirinya sebagai objek jangka pendek, tanpa keadaan. Objek ini sering digunakan untuk memulakan objek lain yang lebih kekal.
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 menghapuskannya dan memindahkan fungsi kecil yang ada pada objek yang berumur panjang. Seterusnya, kita beralih kepada corak yang paling menarik minat kita (sebagai pembangun biasa), iaitu anti-corak pembangunan .

5. Pengekodan keras

Jadi kami telah sampai pada perkataan yang mengerikan ini: pengekodan keras. Intipati anti-corak ini ialah kod itu terikat kuat pada konfigurasi perkakasan dan/atau persekitaran sistem tertentu. Ini sangat merumitkan pemindahan kod ke konfigurasi lain. Anti-corak ini berkait rapat dengan nombor ajaib (anti-corak ini selalunya 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;
}
Sakit, bukan? Di sini kami mengekodkan tetapan sambungan kami. Akibatnya, kod akan berfungsi dengan betul hanya dengan MySQL. Untuk menukar pangkalan data, kita perlu menyelami 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 menggunakan pemalar.

6. Sauh bot

Dalam konteks anti-corak, sauh bot bermaksud menyimpan bahagian sistem yang tidak lagi digunakan selepas melakukan beberapa pengoptimuman atau pemfaktoran semula. Selain itu, beberapa bahagian kod boleh disimpan "untuk kegunaan masa hadapan" sekiranya anda memerlukannya secara tiba-tiba. Pada asasnya, ini menukar kod anda menjadi tong 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 mempunyai kaedah kemas kini yang menggunakan kaedah berasingan untuk menggabungkan data pengguna daripada pangkalan data dengan data pengguna yang dihantar ke kaedah tersebut (jika pengguna menghantar kepada kaedah kemas kini mempunyai medan nol, maka nilai medan lama diambil daripada pangkalan data) . Kemudian andaikan terdapat keperluan baharu bahawa rekod tidak boleh digabungkan dengan yang lama, tetapi sebaliknya, walaupun terdapat medan nol, ia digunakan untuk menulis ganti yang lama:
public User update(Long id, User request) {
   return userDAO.update(user);
}
Ini bermakna mergeUser tidak lagi digunakan, tetapi sayang untuk memadamkannya — bagaimana jika kaedah ini (atau idea kaedah ini) mungkin berguna suatu hari nanti? Kod sedemikian hanya merumitkan sistem dan menimbulkan kekeliruan, yang pada dasarnya tidak mempunyai nilai praktikal. Kita tidak boleh lupa bahawa kod seperti itu dengan "kepingan mati" akan sukar untuk disampaikan kepada rakan sekerja apabila anda pergi untuk projek lain. Cara terbaik untuk menangani sauh bot adalah dengan memfaktorkan semula kod, iaitu memadam bahagian kod (menyayat hati, saya tahu). Di samping itu, semasa menyediakan jadual pembangunan, adalah perlu untuk mengambil kira sauh tersebut (untuk memperuntukkan masa untuk mengemas).

7. Kolam kumbahan objek

Untuk menerangkan anti-corak ini, mula-mula anda perlu membiasakan diri dengan corak kumpulan objek . Kolam objek (kolam sumber) ialah corak reka bentuk ciptaan , set objek yang dimulakan dan sedia untuk digunakan. Apabila aplikasi memerlukan objek, ia diambil daripada kolam ini dan bukannya dicipta semula. Apabila objek tidak lagi diperlukan, ia tidak dimusnahkan. Sebaliknya, ia dikembalikan ke kolam. Corak ini biasanya digunakan untuk objek berat yang memakan masa untuk dibuat setiap kali ia diperlukan, seperti semasa menyambung ke pangkalan data. Mari kita lihat contoh kecil dan mudah. Berikut ialah 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);
   }
}
Kelas ini dipersembahkan dalam bentuk corak tunggal /anti-corak di atas, iaitu hanya terdapat satu objek jenis ini. Ia menggunakan Resourceobjek tertentu. Secara lalai, pembina mengisi kolam dengan 4 kejadian. Apabila anda mendapat objek, ia dialih keluar dari kolam (jika tiada objek yang tersedia, objek itu dibuat dan segera dikembalikan). Dan pada akhirnya, kami mempunyai kaedah untuk meletakkan objek itu kembali. Objek sumber kelihatan 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 mempunyai objek kecil yang mengandungi peta dengan nama corak reka bentuk sebagai kunci dan pautan Wikipedia yang sepadan sebagai nilai, serta kaedah 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);
   }
}
Segala-galanya di sini cukup jelas: kami mendapat objek kolam, dapatkan objek dengan sumber daripada kolam, dapatkan peta daripada objek Sumber, lakukan sesuatu dengannya dan letakkan semua ini pada tempatnya dalam kolam untuk kegunaan semula selanjutnya. Voila, ini ialah corak reka bentuk kolam objek. Tetapi kita bercakap tentang anti-corak, bukan? Mari kita pertimbangkan kes berikut dalam kaedah 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, kami mendapat objek Sumber, kami mendapat peta coraknya, dan kami melakukan sesuatu dengan peta itu. Tetapi sebelum menyimpan peta kembali ke kumpulan objek, ia dikosongkan dan kemudian diisi dengan data yang rosak, menjadikan objek Sumber tidak sesuai untuk digunakan semula. Salah satu butiran utama kumpulan objek ialah apabila objek dikembalikan, ia mesti dipulihkan kepada keadaan yang sesuai untuk digunakan semula selanjutnya. Jika objek yang dikembalikan ke kolam kekal dalam keadaan tidak betul atau tidak ditentukan, maka reka bentuk kami dipanggil kolam kumbahan objek. Adakah masuk akal untuk menyimpan objek yang tidak sesuai untuk digunakan semula? Dalam keadaan ini, kita boleh membuat peta dalaman tidak berubah dalam pembina:
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);
}
Percubaan dan keinginan untuk menukar kandungan peta akan hilang berkat UnsupportedOperationException yang akan mereka hasilkan. Anti-corak ialah perangkap yang sering dihadapi oleh pembangun kerana kekurangan masa, kecuaian, kurang pengalaman atau tekanan daripada pengurus projek. Tergesa-gesa, yang merupakan perkara biasa, boleh membawa kepada masalah besar untuk aplikasi pada masa hadapan, jadi anda perlu mengetahui tentang ralat ini dan mengelakkannya terlebih dahulu. Ini menyimpulkan bahagian pertama artikel. Akan bersambung...
Komen
  • Popular
  • Baru
  • Tua
Anda mesti log masuk untuk meninggalkan ulasan
Halaman ini tidak mempunyai sebarang ulasan lagi