CodeGym /Blog Jawa /Acak /Apa sing anti-pola? Ayo goleki sawetara conto (Bagian 1)
John Squirrels
tingkat
San Francisco

Apa sing anti-pola? Ayo goleki sawetara conto (Bagian 1)

Diterbitake ing grup
Sugeng dina kanggo kabeh! Ing dina liyane aku ana wawancara proyek, lan aku ditakoni sawetara pitakonan babagan anti-pola: apa iku, apa jinis ana, lan apa conto praktis ana. Mesthi wae, aku mangsuli pitakon kasebut, nanging kanthi entheng, amarga aku durung nate nyilem menyang topik iki. Sawise wawancara, aku wiwit scour Internet lan nyemplungaken dhewe liyane lan liyane ing topik. Apa sing anti-pola?  Ayo goleki sawetara conto (Bagian 1) - 1 Dina iki aku pengin menehi ringkesan ringkes babagan anti-pola sing paling populer lan mriksa sawetara conto. Mugi sing maca iki bakal menehi kawruh sing perlu ing wilayah iki. Ayo dadi miwiti! Sadurunge kita ngrembug apa anti-pola, ayo padha kelingan apa pola desain. Pola desainminangka solusi arsitektur sing bisa diulang kanggo masalah utawa kahanan umum sing muncul nalika ngrancang aplikasi. Nanging dina iki kita lagi ora ngomong bab wong-wong mau, nanging rodo opposites - anti-pola. Anti -pola minangka pendekatan sing nyebar nanging ora efektif, beboyo, lan/utawa ora produktif kanggo ngrampungake kelas masalah umum. Ing tembung liya, iki minangka pola kesalahan (uga kadhangkala disebut jebakan). Minangka aturan, anti-pola dipérang dadi jinis ing ngisor iki:
  1. Anti-pola arsitektur - Anti-pola iki muncul nalika struktur sistem dirancang (umume dening arsitek).
  2. Pola anti manajemen / organisasi - Iki minangka pola anti ing manajemen proyek, biasane ditemoni dening macem-macem manajer (utawa klompok manajer).
  3. Pangembangan anti-pola - Anti-pola iki muncul minangka sistem sing ditindakake dening programer biasa.
Rangkaian lengkap anti-pola luwih eksotis, nanging kita ora bakal nganggep kabeh saiki. Kanggo pangembang biasa, sing bakal kakehan. Kanggo wiwitan, ayo nimbang pola anti manajemen minangka conto.

1. Lumpuh analitik

Analisis lumpuhdianggep minangka anti-pola manajemen klasik. Iki kalebu nganalisa kahanan sajrone ngrencanakake, supaya ora ana keputusan utawa tumindak sing ditindakake, ateges nglumpukake proses pangembangan. Iki asring kedadeyan nalika tujuane kanggo nggayuh kasampurnan lan nimbang pancen kabeh sajrone wektu analisis. Anti-pola iki ditondoi dening lumampah ing bunderan (run-of-the-mill tertutup loop), revisi lan nggawe model rinci, kang siji interferes karo alur kerja. Contone, sampeyan nyoba prédhiksi prekara ing tingkat: nanging kepiye yen pangguna dumadakan pengin nggawe dhaptar karyawan adhedhasar huruf kaping papat lan kaping lima saka jenenge, kalebu dhaptar proyek sing paling akeh jam kerja. antarane Taun Anyar lan Dina Wanita Internasional sajrone patang taun kepungkur? Intine, iku' s kakehan analisis. Ing ngisor iki sawetara tips kanggo nglawan paralisis analisis:
  1. Sampeyan kudu nemtokake tujuan jangka panjang minangka mercusuar kanggo nggawe keputusan, supaya saben keputusan sampeyan nyedhaki target tinimbang nyebabake sampeyan stagnate.
  2. Aja konsentrasi ing sepele (kenapa nggawe keputusan babagan rincian sing ora penting kaya-kaya iku keputusan sing paling penting ing urip sampeyan?)
  3. Nyetel deadline kanggo kaputusan.
  4. Aja nyoba ngrampungake tugas kanthi sampurna - luwih becik nindakake kanthi apik.
Ora perlu jero banget ing kene, mula kita ora bakal nganggep pola anti-manajer liyane. Mulane, tanpa introduksi, kita bakal pindhah menyang sawetara anti-pola arsitektur, amarga artikel iki paling kamungkinan bakal diwaca dening pangembang mangsa tinimbang managers.

2. Gusti Allah obyek

Objek Allah minangka anti-pola sing nggambarake konsentrasi sing akeh banget saka kabeh jinis fungsi lan jumlah data sing beda-beda (obyek sing ditrapake aplikasi). 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 kelas gedhe sing nindakake kabeh. Isine pitakon database uga sawetara data. Kita uga ndeleng metode fasad findAllWithoutPageEn, sing kalebu logika bisnis. Obyek Gusti Allah kuwi dadi gedhe banget lan kikuk kanggo njaga kanthi bener. We kudu kekacoan watara karo ing saben Piece saka kode. Akeh komponen sistem gumantung lan digandhengake kanthi rapet. Iku dadi harder lan harder kanggo njaga kode kuwi. Ing kasus kaya mengkono, kode kudu dipérang dadi kelas kapisah, saben kang mung siji waé. Ing conto iki, kita bisa pamisah obyek Dewa 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 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;
   }
                    ....
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

Singleton minangka pola sing paling gampang. Iku mesthekake yen ing aplikasi siji-Utas bakal ana siji Kayata kelas, lan menehi titik akses global kanggo obyek iki. Nanging apa pola utawa anti-pola? Ayo goleki kekurangan saka pola iki:
  1. negara Global Nalika kita ngakses Kayata saka kelas, kita ora ngerti kahanan saiki kelas iki. Kita ora ngerti sapa sing ngganti utawa kapan. Negara bisa uga ora kaya sing dikarepake. Ing tembung liyane, bener nggarap singleton gumantung saka urutan akses menyang. Iki tegese subsistem gumantung siji liyane lan, minangka asil, desain dadi luwih rumit.

  2. Singleton nglanggar prinsip SOLID — prinsip tanggung jawab tunggal: saliyane tugas langsung, kelas singleton uga ngontrol jumlah kedadeyan.

  3. Katergantungan kelas biasa ing singleton ora katon ing antarmuka kelas. Amarga conto singleton ora biasane liwati minangka argumen metode, nanging dijupuk langsung liwat getInstance (), sampeyan kudu njaluk menyang implementasine saben cara kanggo ngenali katergantungan kelas ing singleton - mung katon ing umum kelas. kontrak ora cukup.

    Ing ngarsane singleton nyuda testability saka aplikasi minangka kabèh lan kelas sing nggunakake singleton ing tartamtu. Kaping pisanan, sampeyan ora bisa ngganti singleton karo obyek mock. Kapindho, yen singleton duwe antarmuka kanggo ngganti negara, banjur tes bakal gumantung siji liyane.

    Ing tembung liyane, singleton mundhak kopling, lan kabeh sing kasebut ing ndhuwur ora luwih saka akibat saka tambah kopling.

    Lan yen sampeyan mikir babagan iki, sampeyan bisa ngindhari nggunakake singleton. Contone, bisa uga (lan pancen perlu) nggunakake macem-macem pabrik kanggo ngontrol jumlah kedadeyan obyek.

    Bebaya paling gedhe dumunung ing upaya kanggo mbangun arsitektur aplikasi kabeh adhedhasar singleton. Ana akeh alternatif sing apik kanggo pendekatan iki. Conto sing paling penting yaiku Spring, yaiku wadah IoC: minangka solusi alami kanggo masalah ngontrol nggawe layanan, amarga sejatine "pabrik ing steroid".

    Akeh perdebatan sing ora bisa dipisahake lan ora bisa digabungake saiki ing babagan iki. Sampeyan bisa nemtokake manawa singleton minangka pola utawa anti-pola.

    Kita ora bakal linger ing. Nanging, kita bakal pindhah menyang pola desain pungkasan kanggo dina iki - poltergeist.

4. Poltergeist

A poltergeist minangka anti-pola sing nglibatake kelas tanpa guna sing digunakake kanggo nelpon metode kelas liyane utawa mung nambah lapisan abstraksi sing ora perlu. Anti-pola iki manifests dhewe minangka obyek short-urip, tanpa negara. Obyek iki asring digunakake kanggo initialize liyane, obyek liyane 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);
   }
}
Yagene kita butuh obyek sing mung minangka perantara lan delegasi pakaryane marang wong liya? Kita ngilangi lan nransfer fungsi cilik kasebut menyang obyek sing umure dawa. Sabanjure, kita pindhah menyang pola sing paling menarik kanggo kita (minangka pangembang biasa), yaiku pangembangan anti-pola .

5. Hard coding

Dadi, kita wis tekan tembung iki: hard coding. Inti saka pola anti iki yaiku kode kasebut diikat banget karo konfigurasi hardware lan / utawa lingkungan sistem tartamtu. Iki rumit banget kanggo ngirim kode menyang konfigurasi liyane. Anti-pola iki raket banget karo angka-angka ajaib (anti-pola iki asring digandhengake). 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;
}
Nyeri, ta? Kene kita hard kode setelan sambungan kita. Akibaté, kode mung bakal bisa digunakake kanthi bener karo MySQL. Kanggo ngganti database, kita kudu nyilem menyang 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 yaiku nggunakake konstanta.

6. Jangkar prau

Ing konteks anti-pola, jangkar prau tegese tetep bagean saka sistem sing ora digunakake maneh sawise nindakake sawetara optimasi utawa refactoring. Uga, sawetara bagéan saka kode bisa disimpen "kanggo mangsa ngarep" mung yen sampeyan dumadakan perlu. Intine, iki ngowahi kode sampeyan dadi tong sampah. Tuladha:

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 metode sing kapisah kanggo nggabungake data pangguna saka database karo data pangguna sing diterusake menyang metode kasebut (yen pangguna liwat metode nganyari duwe kolom null, banjur nilai lapangan lawas dijupuk saka database) . Banjur umpamane ana syarat anyar yen cathetan kasebut ora kudu digabung karo sing lawas, nanging, sanajan ana kolom kosong, mula digunakake kanggo nimpa sing lawas:

public User update(Long id, User request) {
   return userDAO.update(user);
}
Iki tegese mergeUser wis ora digunakake maneh, nanging bakal dadi sayang kanggo mbusak - apa yen cara iki (utawa gagasan cara iki) bisa migunani ing sawijining dina? Kode kuwi mung complicates sistem lan ngenalaken kebingungan, gadhah ateges ora ana nilai praktis. Kita ora kudu lali yen kode kasebut kanthi "potongan mati" bakal angel diterusake menyang kolega nalika sampeyan lunga menyang proyek liyane. Cara paling apik kanggo ngatasi jangkar prau yaiku refactor kode kasebut, yaiku mbusak bagean kode (aku ngerti). Kajaba iku, nalika nyiapake jadwal pembangunan, perlu kanggo nyathet jangkar kasebut (kanggo nyedhiakake wektu kanggo ngresiki).

7. Obyek cesspool

Kanggo njlèntrèhaké anti-pola iki, pisanan sampeyan kudu njaluk kenalan karo pola blumbang obyek . Kolam obyek (blumbang sumber) minangka pola desain kreasi , sakumpulan obyek sing wis diwiwiti lan siap digunakake. Nalika aplikasi mbutuhake obyek, dijupuk saka blumbang iki tinimbang digawe maneh. Nalika obyek ora perlu maneh, iku ora numpes. Nanging, bali menyang blumbang. Pola iki biasane digunakake kanggo obyek abot sing mbutuhake wektu kanggo nggawe saben wektu dibutuhake, kayata nalika nyambung menyang database. Ayo katon ing conto cilik lan prasaja. Ing ngisor iki 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);
   }
}
Klas punika dipunandharaken kanthi wujud pola tunggal /anti-pola ing nginggil, inggih punika saged namung satunggal objek saking jinis punika. Iku nggunakake Resourceobyek tartamtu. Kanthi gawan, konstruktor ngisi blumbang kanthi 4 conto. Nalika sampeyan entuk obyek, dibusak saka blumbang (yen ora ana obyek sing kasedhiya, siji digawe lan langsung bali). Lan ing pungkasan, kita duwe cara kanggo nyelehake obyek kasebut maneh. Objek sumber katon kaya iki:

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;
   }
}
Ing kene kita duwe obyek cilik sing ngemot peta kanthi jeneng pola desain minangka kunci lan pranala Wikipedia sing cocog minangka nilai, uga cara kanggo ngakses peta. Ayo goleki 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);
   }
}
Kabeh ing kene cukup cetha: kita entuk obyek blumbang, entuk obyek kanthi sumber daya saka blumbang, entuk peta saka obyek Sumber daya, nindakake apa wae, lan sijine kabeh iki ing blumbang kanggo nggunakake maneh. Voila, iki pola desain blumbang obyek. Nanging kita ngomong babagan anti-pola, ta? Ayo nimbang kasus ing ngisor iki ing cara 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);
Ing kene, maneh, kita entuk obyek Sumber Daya, entuk peta pola, lan nindakake apa wae karo peta kasebut. Nanging sadurunge nyimpen peta bali menyang blumbang obyek, iku dibusak lan banjur diisi karo data rusak, nggawe obyek Resource ora cocog kanggo nggunakake maneh. Salah sawijining rincian utama blumbang obyek yaiku nalika obyek bali, kudu dipulihake menyang negara sing cocog kanggo digunakake maneh. Yen obyek bali menyang blumbang tetep ing negara salah utawa undefined, banjur desain kita disebut cesspool obyek. Apa ana gunane kanggo nyimpen obyek sing ora cocog kanggo digunakake maneh? Ing kahanan iki, kita bisa nggawe peta internal ora bisa diganti ing 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);
}
Usaha lan kepinginan kanggo ngganti isi peta bakal ilang amarga UnsupportedOperationException sing bakal diasilake. Anti-pola minangka jebakan sing kerep ditemokake para pangembang amarga kekurangan wektu, kecerobohan, pengalaman, utawa tekanan saka manajer proyek. Rushing, sing umum, bisa nyebabake masalah gedhe kanggo aplikasi ing mangsa ngarep, dadi sampeyan kudu ngerti babagan kesalahan kasebut lan nyingkiri sadurunge. Iki rampung bagean pisanan saka artikel. Diterusake...
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION