JavaRush /Blog Jawa /Random-JV /Apa antipatterns? Ayo ndeleng conto (bagean 1)

Apa antipatterns? Ayo ndeleng conto (bagean 1)

Diterbitake ing grup
Apa antipatterns?  Ayo ndeleng conto (bagean 1) - 1Sugeng dina kanggo kabeh! Ing dina liyane aku diwawancarai, lan aku ditakoni babagan antipatterns: kewan apa iki, apa jinis lan conto ing laku. Mesthi wae, aku mangsuli pitakon kasebut, nanging kanthi entheng, amarga aku ora sinau babagan masalah iki. Sawise wawancara, aku wiwit scour Internet, dadi liyane lan liyane nyemplungaken ing topik iki. Dina iki aku kaya kanggo nggawe review singkat saka antipatterns paling populer lan contone, maca kang bisa menehi kawruh perlu ing masalah iki. Ayo dadi miwiti! Dadi, sadurunge ngrembug apa antipattern, ayo padha ngelingi apa pola. Pola minangka desain arsitektur sing bisa diulang kanggo ngrampungake masalah utawa kahanan umum sing muncul nalika ngrancang aplikasi. Nanging dina iki kita ora ngomong bab wong-wong mau, nanging bab opposites - antipatterns. Anti-pola minangka pendekatan umum kanggo ngrampungake kelas masalah sing umum ditemoni sing ora efektif, beboyo, utawa ora produktif. Ing tembung liyane, iku pola kesalahan (uga kadhangkala disebut trap). Apa antipatterns?  Ayo ndeleng conto (bagean 1) - 2Minangka aturan, antipatterns dipérang dadi jinis ing ngisor iki:
  1. Antipattern arsitektur - antipattern arsitektur sing muncul nalika ngrancang struktur sistem (biasane dening arsitek).
  2. Manajemen Anti Pola - antipatterns ing bidang manajemen, sing biasane ditemoni dening macem-macem manajer (utawa klompok manajer).
  3. Development Anti Pattern - antipatterns minangka masalah pangembangan sing muncul nalika programer biasa nulis sistem.
Eksotisme antipattern luwih jembar, nanging kita ora bakal nganggep dina iki, amarga kanggo pangembang biasa, iki bakal dadi akeh banget. Kanggo miwiti, ayo njupuk conto antipattern ing bidang manajemen.

1. Lumpuh analitik

Kelumpuhan analisis dianggep minangka anti-pola organisasi klasik. Iku kalebu overanalyzing kahanan nalika planning supaya ora ana kaputusan utawa tumindak dijupuk, ateges pembangunan lumpuh. Iki asring kedadeyan nalika tujuane kanggo nggayuh kesempurnaan lan ngrampungake periode analisis. Anti-pola iki ditondoi kanthi mlaku-mlaku ing bunderan (jinis loop tertutup), revisi lan nggawe model sing rinci, sing banjur ngganggu alur kerja. Contone, sampeyan nyoba prédhiksi kaya: kepiye yen pangguna dumadakan pengin nggawe dhaptar karyawan adhedhasar huruf kaping papat lan kaping lima saka jenenge, kalebu ing dhaptar proyek sing paling akeh digunakake ing antarane jam kerja. Taun Anyar lan Kawolu Maret ing patang taun sadurunge? Intine, iki minangka analisis sing overabundance. Conto nyata sing apik yaiku kepiye paralysis analisis nyebabake Kodak bangkrut . Mangkene sawetara tips cilik kanggo nglawan paralysis analisis:
  1. Sampeyan kudu nemtokake tujuan jangka panjang minangka mercusuar kanggo nggawe keputusan, supaya saben keputusan sing sampeyan lakoni nggawa sampeyan luwih cedhak karo tujuan sampeyan, lan ora meksa sampeyan menehi tandha wektu.
  2. Aja konsentrasi ing sepele (kenapa nggawe keputusan babagan nuansa cilik kaya sing pungkasan ing urip sampeyan?)
  3. Nyetel deadline kanggo nggawe keputusan.
  4. Aja nyoba nindakake tugas kanthi sampurna: luwih becik nindakake kanthi apik.
Kita ora bakal jero banget lan nimbang antipattern manajemen liyane saiki. Mulane, tanpa preamble, ayo pindhah menyang sawetara antipatterns arsitektur, amarga paling kamungkinan, artikel iki diwaca dening pangembang mangsa, ora manager.

2. Gusti Allah obyek

Objek gaib minangka anti-pola sing nggambarake konsentrasi kakehan fungsi sing beda-beda, nyimpen akeh data sing beda-beda (obyek ing saubengé aplikasi kasebut). Ayo njupuk conto cilik:
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();
   }
}
Ing kene kita ndeleng sawetara kelas gedhe sing nindakake kabeh bebarengan. Ngandhut pitakon menyang Database, ngemot sawetara data, kita uga ndeleng cara fasad findAllWithoutPageEnkanthi logika bisnis. Obyek gaib kuwi dadi gedhe lan kikuk kanggo ndhukung cekap. We kudu tinker karo ing saben Piece saka kode: akeh kelenjar ing sistem gumantung ing lan tightly gandheng menyang. Njaga kode kasebut dadi luwih angel. Ing kasus kaya mengkono, iku kudu dipérang dadi kelas kapisah, saben kang mung siji waé (goal). Ing conto iki, sampeyan bisa dipérang dadi 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 sing ngemot data lan cara kanggo ngakses:
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;
   }
                    ....
Lan luwih cocog kanggo mindhah metode kanthi logika bisnis menyang 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. Tunggal

A singleton minangka pola paling gampang sing njamin bakal ana siji conto saka sawetara kelas ing aplikasi siji-threaded lan nyedhiyakake jalur akses global kanggo obyek kasebut. Sampeyan bisa maca liyane babagan kene . Nanging iki pola utawa antipattern? Apa antipatterns?  Ayo ndeleng conto (bagean 1) - 3Ayo goleki kekurangane cithakan iki:
  1. negara global. Nalika kita ngakses Kayata saka kelas, kita ora ngerti kahanan saiki kelas utawa sing diganti utawa nalika, lan negara bisa uga ora kita nyana. Ing tembung liyane, bener saka karya karo singleton gumantung ing urutan saka telpon, kang nimbulaké subsistem gumantung ing saben liyane lan, minangka asil, akeh nambah kerumitan pembangunan.

  2. Singleton nglanggar salah siji prinsip SOLID - Prinsip Tanggung Jawab Tunggal - kelas Singleton, saliyane nindakake tanggung jawab langsung, uga ngontrol jumlah kedadeyane.

  3. Ketergantungan kelas biasa ing singleton ora katon ing antarmuka kelas. Wiwit biasane conto saka singleton ora liwati ing paramèter saka cara, nanging dijupuk langsung, liwat getInstance(), kanggo ngenali katergantungan kelas ing singleton, sampeyan kudu delve menyang implementasine saben cara - mung ndeleng umum. kontrak saka obyek ora cukup.

    Ing ngarsane singleton nyuda testability saka aplikasi ing umum lan kelas sing nggunakake singleton ing tartamtu. Sepisanan, sampeyan ora bisa sijine obyek Mock ing panggonan singleton, lan sareh, yen singleton duwe antarmuka kanggo ngganti negara, tes bakal gumantung ing saben liyane.

    Ing tembung liyane, singleton nambah panyambungan, lan kabeh ing ndhuwur ora luwih saka akibat saka tambah panyambungan.

    Lan yen sampeyan mikir babagan iki, nggunakake singleton bisa nyingkiri. Contone, kanggo ngontrol jumlah kedadean obyek, iku cukup bisa (lan perlu) kanggo nggunakake macem-macem pabrik.

    Bebaya paling gedhe dumunung ing upaya kanggo mbangun kabeh arsitektur aplikasi adhedhasar singletons. Ana akeh alternatif sing apik kanggo pendekatan iki. Conto sing paling penting yaiku Spring, yaiku kontaner IoC: ana masalah ngontrol nggawe layanan ditanggulangi kanthi alami, amarga dheweke, nyatane, minangka "pabrik ing steroid".

    Saiki ana akeh holivar babagan topik iki, mula sampeyan kudu mutusake manawa singleton minangka pola utawa antipattern.

    Lan kita ora bakal ngelingi lan pindhah menyang pola desain pungkasan kanggo dina iki - poltergeist.

4. Poltergeist

Poltergeist minangka antipattern kelas sing ora migunani sing digunakake kanggo nelpon metode kelas liyane utawa mung nambah lapisan abstraksi sing ora perlu. Antipattern manifests dhewe ing wangun obyek short-urip tanpa negara. Objek kasebut asring digunakake kanggo miwiti obyek liyane sing luwih awet.
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);
   }
}
Yagene kita butuh obyek sing mung minangka perantara lan delegasi pakaryane marang wong liya? Kita mbusak, lan mindhah fungsi cilik sing dileksanakake menyang obyek long-urip. Sabanjure, kita pindhah menyang pola sing paling menarik kanggo kita (minangka pangembang biasa) - development antipatterns .

5. Kode hard

Dadi kita entuk tembung sing nggegirisi iki - hardcode. Inti saka antipattern iki sing kode banget disambungake menyang konfigurasi hardware tartamtu lan / utawa lingkungan sistem, kang ndadekake banget angel kanggo port kanggo konfigurasi liyane. Antipattern iki raket banget karo nomer sihir (asring intertwined). Tuladha:
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, ta? Ing kene kita langsung nyetel konfigurasi sambungan kita; minangka asil, kode mung bakal bisa digunakake kanthi bener karo MySQL, lan kanggo ngganti database sampeyan kudu mlebu kode lan ngganti kabeh kanthi manual. Solusi sing apik yaiku nyelehake konfigurasi ing file sing kapisah:
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 liyane kanggo mindhah menyang konstanta.

6. Jangkar prau

Jangkar prau ing konteks antipattern tegese nyimpen bagean sing ora digunakake saka sistem sing ditinggalake sawise sawetara optimasi utawa refactoring. Uga, sawetara bagéan saka kode bisa ditinggalake "kanggo mangsa ngarep", yen sampeyan kudu nggunakake maneh. Iki ateges kode kasebut dadi tong sampah. Apa antipatterns?  Ayo ndeleng conto (bagean 1) - 4Tuladha:
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());
}
Kita duwe metode nganyari sing nggunakake cara sing kapisah kanggo nggabungake data pangguna saka database lan sing teka kanggo nganyari (yen wong sing teka kanggo nganyari duwe kolom kosong, banjur ditulis minangka sing lawas. saka database). Contone, ana syarat yen cathetan ora kudu digabung karo sing lawas, nanging ditimpa, sanajan ana kolom kosong:
public User update(Long id, User request) {
   return userDAO.update(user);
}
Akibaté, mergeUseriku wis ora digunakake maneh lan iku sayang kanggo mbusak iku: apa yen (utawa idea) isih migunani? Kode kuwi mung complicates lan mbingungake sistem, ateges ora nyedhiyani sembarang nilai praktis. Kita ora kudu lali yen kode kasebut kanthi "potongan mati" bakal angel ditransfer menyang kolega nalika sampeyan lunga menyang proyek liyane. Cara paling apik kanggo ngatasi jangkar prau yaiku refactoring kode, yaiku, mbusak bagean kode kasebut (alas, sayang). Uga, nalika ngrancang pembangunan, sampeyan kudu nganggep kedadeyan jangkar kasebut (ngidini wektu kanggo ngresiki tailing).

7. Obyek cesspool

Kanggo njlèntrèhaké antipattern iki, pisanan sampeyan kudu kenalan karo pola blumbang obyek . Kolam obyek (blumbang sumber) minangka pola desain generatif , sakumpulan obyek sing diinisialisasi lan siap digunakake. Nalika aplikasi mbutuhake obyek, ora digawe anew, nanging dijupuk saka blumbang iki. Nalika obyek ora perlu maneh, iku ora numpes, nanging bali menyang blumbang. Biasane digunakake kanggo obyek abot sing sumber daya-intensif kanggo nggawe saben wektu, kayata sambungan database. Ayo ndeleng conto cilik lan prasaja kanggo conto. Dadi, kita duwe kelas sing makili pola iki:
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);
   }
}
We saiki kelas iki ing wangun pola singleton ndhuwur-diterangake / antipattern , sing, ana mung siji obyek saka jinis iki, makaryakke ing obyek tartamtu Resource, minangka standar ing konstruktor blumbang kapenuhan 4 salinan; nalika obyek kasebut dijupuk, dibuwang saka blumbang (yen ora ana, digawe lan langsung diwenehi), lan ing pungkasan ana cara kanggo nyelehake obyek kasebut maneh. Objek Resourcekaton kaya iki:
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;
   }
}
Ing kene kita duwe obyek cilik sing ngemot peta kanthi jeneng pola minangka kunci lan ngubungake minangka nilai, uga cara kanggo ngakses peta. Ayo katon 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);
   }
}
Kabeh ing kene uga cetha: kita njupuk obyek blumbang, narik metu obyek karo sumber daya saka iku, njupuk peta saka iku, nindakake soko karo lan sijine kabeh maneh ing blumbang kanggo nggunakake maneh. Voila: kene sampeyan duwe pola blumbang obyek. Nanging kita ngomong babagan antipattern, ta? Ayo ndeleng kasus iki 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);
Ing kene maneh, obyek sumber daya dijupuk, peta kanthi pola dijupuk lan ana sing ditindakake, nanging sadurunge disimpen maneh menyang blumbang obyek, peta kasebut diresiki lan diisi data sing ora bisa dingerteni sing ndadekake obyek Sumber Daya iki ora cocog kanggo digunakake maneh. Salah sawijining nuansa utama blumbang obyek yaiku sawise obyek bali, kudu bali menyang negara sing cocog kanggo digunakake maneh. Yen obyek ing negara salah utawa undefined sawise bali menyang blumbang, mbangun iki disebut cesspool obyek. Apa gunane nyimpen barang sing ora bisa digunakake maneh? Ing kahanan iki, sampeyan bisa nggawe peta internal ora bisa diganti ing 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 lan kepinginan kanggo ngganti isi bakal tiba bebarengan karo UnsupportedOperationException). Antipattern minangka jebakan sing asring ditindakake para pangembang amarga kekurangan wektu, ora nggatekake, ora pengalaman, utawa tendhangan saka manajer. Kurang wektu lan cepet-cepet bisa nyebabake masalah gedhe kanggo aplikasi ing mangsa ngarep, mula kesalahan kasebut kudu dingerteni lan dihindari sadurunge. Apa antipatterns?  Ayo ndeleng conto (bagean 1) - 6Kanthi iki, bagean pisanan saka artikel wis rampung: diterusake .
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION