Šimtakojų metodo sutramdymas: paprasto, prižiūrimo kodo rašymo strategijos

Šimtakojų metodo sutramdymas: paprasto, prižiūrimo kodo rašymo strategijos


Kai buvau vaikas, gulėdavau ant lovos ir ilgai žiūrėdavau į seno sovietinio kilimėlio raštus, juose matydama gyvūnus ir fantastiškas figūras. Dabar dažniau žiūriu į kodą, bet galvoje vis dar kyla panašūs vaizdai. Kaip ir ant kilimėlio, šie vaizdai sudaro pasikartojančius raštus. Jie gali būti malonūs arba atstumti. Šiandien noriu papasakoti apie vieną tokį nemalonų modelį, kurį galima rasti programuojant.

Įsivaizduokite paslaugą, kuri apdoroja kliento registracijos užklausą ir siunčia apie tai įvykį Kafkai. Šiame straipsnyje pateiksiu įgyvendinimo pavyzdį, kurį laikau antipatternu, ir pasiūlysiu patobulintą versiją.

1 variantas: šimtakojų metodas

Žemiau pateiktas Java kodas rodo kodą RegistrationService klasė, kuri apdoroja užklausą ir siunčia įvykį.

public class RegistrationService {

    private final ClientRepository clientRepository;
    private final KafkaTemplate<Object, Object> kafkaTemplate;
    private final ObjectMapper objectMapper;

    public void registerClient(RegistrationRequest request) {
        var client = clientRepository.save(Client.builder()
                .email(request.email())
                .firstName(request.firstName())
                .lastName(request.lastName())
                .build());
        sendEvent(client);
    }

    @SneakyThrows
    private void sendEvent(Client client) {
        var event = RegistrationEvent.builder()
                .clientId(client.getId())
                .email(client.getEmail())
                .firstName(client.getFirstName())
                .lastName(client.getLastName())
                .build();
        Message message = MessageBuilder
                .withPayload(objectMapper.writeValueAsString(event))
                .setHeader(KafkaHeaders.TOPIC, "topic-registration")
                .setHeader(KafkaHeaders.KEY, client.getEmail())
                .build();
        kafkaTemplate.send(message).get();
    }

    @Builder
    public record RegistrationEvent(int clientId, String email, String firstName, String lastName) {}
}

Kodo struktūrą galima supaprastinti taip:

Čia matote, kad metodai sudaro nenutrūkstamą grandinę, per kurią perduodami duomenys, kaip per ilgą, siaurą žarnyną. Šios grandinės viduryje esantys metodai yra atsakingi ne tik už logiką, tiesiogiai aprašytą jų turinyje, bet ir už metodų, kuriuos jie iškviečia, ir jų sutarčių logiką (pvz., poreikį tvarkyti konkrečias klaidas). Visi metodai, buvę prieš iškviestą, paveldi visą jo sudėtingumą. Pavyzdžiui, jei kafkaTemplate.send turi šalutinį poveikį, kai siunčiamas įvykis, tada skambinama sendEvent metodas taip pat įgauna tą patį šalutinį poveikį. The sendEvent metodas taip pat tampa atsakingas už serializavimą, įskaitant jo klaidų tvarkymą. Atskirų kodo dalių testavimas tampa sudėtingesnis, nes nėra jokio būdo išbandyti kiekvieną dalį atskirai, nenaudojant maketų.

2 variantas: patobulinta versija

public class RegistrationService {

    private final ClientRepository clientRepository;
    private final KafkaTemplate<Object, Object> kafkaTemplate;
    private final ObjectMapper objectMapper;

    @SneakyThrows
    public void registerClient(RegistrationController.RegistrationRequest request) {
        var client = clientRepository.save(Client.builder()
                .email(request.email())
                .firstName(request.firstName())
                .lastName(request.lastName())
                .build());
        Message<String> message = mapToEventMessage(client);
        kafkaTemplate.send(message).get();
    }

    private Message<String> mapToEventMessage(Client client) throws JsonProcessingException {
        var event = RegistrationEvent.builder()
                .clientId(client.getId())
                .email(client.getEmail())
                .firstName(client.getFirstName())
                .lastName(client.getLastName())
                .build();
        return MessageBuilder
                .withPayload(objectMapper.writeValueAsString(event))
                .setHeader(KafkaHeaders.TOPIC, "topic-registration")
                .setHeader(KafkaHeaders.KEY, event.email)
                .build();
    }

    @Builder
    public record RegistrationEvent(int clientId, String email, String firstName, String lastName) {}
}

Diagrama parodyta žemiau:

Čia galite pamatyti, kad sendEvent metodo visiškai nėra, ir kafkaTemplate.send yra atsakingas už žinutės išsiuntimą. Visas pranešimo Kafkai kūrimo procesas buvo perkeltas į atskirą mapToEventMessage metodas.

The mapToEventMessage metodas neturi šalutinio poveikio, o jo atsakomybės ribos yra aiškiai apibrėžtos. Išimtys, susijusios su serializavimu ir pranešimų siuntimu, yra atskirų metodų sutarčių dalis ir gali būti tvarkomos atskirai.

The mapToEventMessage metodas yra gryna funkcija. Kai funkcija yra deterministinė ir neturi šalutinio poveikio, vadiname „gryna“ funkcija. Grynosios funkcijos yra:

  • lengviau skaityti,
  • lengviau derinti,
  • lengviau išbandyti,
  • nepriklausomai nuo to, kokia tvarka jie vadinami,
  • paprasta paleisti lygiagrečiai.

Rekomendacijos

Siūlyčiau šiuos metodus, kurie gali padėti išvengti tokių antipatternų kode:

  • Trofėjų metodas
  • Vienos krūvos technika
  • Bandymu pagrįsta plėtra (TDD)

Visos šios technikos yra glaudžiai susijusios ir viena kitą papildo.

Testavimo trofėjus

Tai bandymo aprėpties metodas, kuriame akcentuojami integravimo testai, kuriais patikrinama visa paslaugos sutartis. Vienetų testai naudojami atskiroms funkcijoms, kurias sudėtinga arba brangu patikrinti atliekant integravimo testus. Šio metodo testus aprašiau savo straipsniuose: Veiksmingų integravimo testų rašymas pavasarį: organizuotos testavimo strategijos HTTP užklausų pasityčiojimui, integracijos testo skaidrumo gerinimas, patikimas testavimas naudojant Kafka: izoliavimo metodai.

Viena krūva

Ši technika aprašyta Kento Becko knygoje „Tidy First?” Pagrindinė mintis yra ta, kad skaityti ir suprasti kodą yra sunkiau nei jį rašyti. Jei kodas yra suskaidytas į per daug mažų dalių, gali būti naudinga pirmiausia jį sujungti į visumą, kad pamatytumėte bendrą struktūrą ir logiką, o tada vėl suskaidyti į suprantamesnes dalis.

Šio straipsnio kontekste siūloma neskaidyti kodo į metodus, kol jis neužtikrins reikiamos sutarties įvykdymo.

Bandymu pagrįstas kūrimas

Šis metodas leidžia atskirti kodo rašymo, siekiant įgyvendinti sutartį, ir kodo projektavimo pastangas. Mes nesistengiame vienu metu sukurti gero dizaino ir parašyti reikalavimus atitinkančio kodo, o šias užduotis atskiriame.

Kūrimo procesas atrodo taip:

  1. Parašykite paslaugų sutarties testus naudodami testavimo trofėjaus metodą.
  2. Parašykite kodą „One Pile“ stiliumi, užtikrindami, kad jis atitiktų reikiamą sutartį. Nesijaudinkite dėl kodo dizaino kokybės.
  3. Refaktorizuoti kodą. Visas kodas yra parašytas, o mes puikiai suprantame įgyvendinimą ir galimas kliūtis.

Išvada

Straipsnyje aptariamas antipatterno pavyzdys, dėl kurio gali kilti sunkumų prižiūrint ir išbandant kodą. Tokie metodai kaip „Testing Trophy“, „One Pile“ ir „Test-Driven Development“ leidžia struktūrizuoti savo darbą taip, kad kodas nepavirstų neįveikiamu labirintu. Investuodami laiką į tinkamą kodo organizavimą, padedame pagrindą mūsų programinės įrangos produktų ilgalaikiam tvarumui ir nesudėtingai priežiūrai.

Dėkojame už dėmesį straipsniui ir linkime sėkmės rašant paprastą kodą!



Source link

Draugai: - Marketingo agentūra - Teisinės konsultacijos - Skaidrių skenavimas - Fotofilmų kūrimas - Miesto naujienos - Šeimos gydytojai - Saulius Narbutas - Įvaizdžio kūrimas - Veidoskaita - Nuotekų valymo įrenginiai - Teniso treniruotės - Pranešimai spaudai -