December 19, 2025 (4mo ago) — last updated February 10, 2026 (2mo ago)

Wzorzec Adaptera: przykłady w TypeScript, React i Node.js

Dowiedz się, jak wzorzec Adaptera łączy niekompatybilne interfejsy na przykładach w TypeScript, React i Node.js, aby unowocześnić integracje i istniejący kod.

← Back to blog
Cover Image for Wzorzec Adaptera: przykłady w TypeScript, React i Node.js

Dowiedz się, jak wzorzec Adaptera łączy niekompatybilne interfejsy na przykładach w TypeScript, React i Node.js, aby unowocześnić integracje i istniejący kod.

Wzorzec Adaptera: TypeScript, React & Node.js — przykłady

Podsumowanie: Dowiedz się, jak wzorzec Adaptera łączy niekompatybilne interfejsy w TypeScript, React i Node.js na praktycznych, rzeczywistych przykładach.

Wprowadzenie

Czy kiedykolwiek miałeś całkiem dobrą bibliotekę lub moduł legacy, który po prostu nie pasuje do reszty systemu? To jak próba włożenia europejskiej wtyczki do gniazdka północnoamerykańskiego — obie działają, ale ich interfejsy się nie zgadzają. Wzorzec Adaptera rozwiązuje to, tłumacząc jeden interfejs na inny, dzięki czemu możesz ponownie wykorzystać istniejący kod bez jego zmieniania.

Ten przewodnik wyjaśnia wzorzec, pokazuje przykłady w TypeScript i React oraz demonstruje adapter w Node.js, który unowocześnia moduły oparte na callbackach. Skupia się na praktycznych technikach, które możesz zastosować od razu, aby uporządkować integracje i poprawić testowalność.

Dlaczego wzorzec Adaptera ma znaczenie

Wzorzec Adaptera to wzorzec strukturalny, który opakowuje niekompatybilny obiekt i udostępnia interfejs oczekiwany przez twój kod. Został po raz pierwszy opisany przez Gang of Four w 1994 roku1. Adaptery są niezbędne do integrowania API firm trzecich, unowocześniania starego kodu oraz unifikowania rozproszonych źródeł danych.

Typowe scenariusze, w których adaptery pomagają:

  • Integracja API firm trzecich, które zwracają dane o różnych kształtach.
  • Unowocześnianie starych API opartych na callbackach, aby działały z async/await.
  • Unifikowanie wielu źródeł danych w jeden interfejs dla komponentów UI.

Użycie adaptera utrzymuje logikę biznesową czystą i odseparowaną od systemów zewnętrznych.

Wzorzec Adaptera w skrócie

ConceptDescription
TypeStructural
Primary intentAllow objects with incompatible interfaces to work together
Core ideaWrap the adaptee to expose the target interface
Key problem solvedReuse existing classes without changing their source code
Common use casesThird-party libraries, legacy code, multiple data sources

Struktura i role

Wzorzec ma cztery role:

  1. The Client — kod, który potrzebuje konkretnego interfejsu.
  2. The Target Interface — kontrakt, którego oczekuje Client.
  3. The Adaptee — niekompatybilna klasa lub moduł z potrzebną funkcjonalnością.
  4. The Adapter — implementuje Target i deleguje do Adaptee, tłumacząc wywołania w razie potrzeby.

Dwa powszechne style adapterów:

  • Object adapter (kompozycja): Adapter posiada instancję Adaptee. To najbardziej elastyczne podejście.
  • Class adapter (dziedziczenie): Adapter dziedziczy po Adaptee i implementuje Target. Wymaga wielodziedziczenia i jest mniej powszechne we współczesnym JavaScript i TypeScript.

Praktyczny przykład: TypeScript + React

Wyobraź sobie dashboard, który otrzymuje profile użytkowników z dwóch usług o różnych kształtach odpowiedzi. Bez adaptera komponenty zaczynają być pełne logiki warunkowej.

Niekompatybilne kształty API

// Data from UserServiceA
interface UserA {
  userId: number;
  fullName: string;
  emailAddress: string;
}

// Data from UserServiceB
interface UserB {
  id: string;
  name: string;
  contact: {
    email: string;
  };
}

Interfejs docelowy, którego oczekuje nasza aplikacja

interface UnifiedUser {
  id: string;
  name: string;
  email: string;
}

Adaptery w TypeScript

// Adapter for UserServiceA
function adaptUserA(userA: UserA): UnifiedUser {
  return {
    id: userA.userId.toString(),
    name: userA.fullName,
    email: userA.emailAddress,
  };
}

// Adapter for UserServiceB
function adaptUserB(userB: UserB): UnifiedUser {
  return {
    id: userB.id,
    name: userB.name,
    email: userB.contact.email,
  };
}

Centralizacja transformacji utrzymuje komponenty czyste i odporne na zmiany. Jeśli API zmieni nazwę pola, zmienia się tylko adapter.

Komponent React konsumujący zunifikowane dane

interface UserProfileProps {
  user: UnifiedUser;
}

const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>ID: {user.id}</p>
      <p>Email: {user.email}</p>
    </div>
  );
};

Ten komponent polega na jednym, przewidywalnym kształcie danych, co ułatwia testowanie i ponowne użycie.

Przykład: unowocześnianie modułu Node.js opartego na callbackach

Stare moduły często używają callbacków z pierwszym argumentem będącym błędem (error-first callbacks). Zamiast modyfikować stabilny moduł, zbuduj adapter, który udostępnia API oparte na Promise.

Adaptee legacy (nie modyfikować)

// legacyFileProcessor.js
const fs = require('fs');

class LegacyFileProcessor {
  processFile(filePath, callback) {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        return callback(err, null);
      }
      const processedContent = data.toUpperCase();
      callback(null, processedContent);
    });
  }
}

module.exports = LegacyFileProcessor;

Adapter zwracający Promise

// FileProcessorAdapter.js
const LegacyFileProcessor = require('./legacyFileProcessor');

class FileProcessorAdapter {
  constructor() {
    this.legacyProcessor = new LegacyFileProcessor();
  }

  processFile(filePath) {
    return new Promise((resolve, reject) => {
      this.legacyProcessor.processFile(filePath, (err, data) => {
        if (err) return reject(err);
        resolve(data);
      });
    });
  }
}

module.exports = FileProcessorAdapter;

To odzwierciedla zachowanie util.promisify w Node, ale utrzymuje logikę adaptacji jawnie i łatwo testowalną3.

Użycie adaptera w kodzie aplikacji

const FileProcessorAdapter = require('./FileProcessorAdapter');
const fileProcessor = new FileProcessorAdapter();

async function handleFileProcessing() {
  try {
    console.log('Processing file with modern async/await...');
    const content = await fileProcessor.processFile('my-file.txt');
    console.log('Processed Content:', content);
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

handleFileProcessing();

To pozwala pozostawić stary kod nietknięty, jednocześnie udostępniając reszcie bazy kodu nowoczesny interfejs.

Kiedy używać adaptera

Użyj adaptera, gdy dwa komponenty nie mogą komunikować się bezpośrednio, ponieważ ich interfejsy się różnią. Typowe scenariusze:

  • Integracja API firm trzecich, których wejścia lub wyjścia nie pasują do twoich modeli.
  • Opakowywanie starych API opartych na callbackach, aby działały z async/await.
  • Obsługa wielu źródeł danych o różnych formatach poprzez stworzenie jednego adaptera na źródło.

Kiedy nie używać adaptera:

  • Jeśli kontrolujesz obie strony i mała refaktoryzacja rozwiąże problem niezgodności, lepiej przeprowadzić bezpośrednią refaktoryzację.
  • Jeśli celem jest uproszczenie złożonego podsystemu, rozważ zamiast tego Fasadę (Facade). Fasada oferuje uproszczony, wysokopoziomowy punkt wejścia; Adapter skupia się wyłącznie na kompatybilności.

Szybka checklista decyzji

SituationUse Adapter?Why
Need to use a third-party library with incompatible APIYesYou can’t change the library, so adapt to it
Control both sides and change is smallNoRefactor directly to avoid extra indirection
Need a simplified high-level interface to a complex systemNoFacade is a better fit
Migrating legacy systems incrementallyYesWrap old components to match new interfaces
Multiple differently structured data sourcesYesAdapters unify them into one shape

Testowanie i wydajność

Adaptery poprawiają testowalność przez odseparowanie logiki rdzenia od systemów zewnętrznych. Możesz zmockować interfejs adaptera, aby testować komponenty w izolacji, a same adaptery testować oddzielnie, aby zweryfikować logikę tłumaczenia.

Nadwyżka wydajnościowa związana z adapterem jest minimalna — zazwyczaj jedno dodatkowe wywołanie funkcji — i jest pomijalna w porównaniu z operacjami sieciowymi czy zapytaniami do bazy danych. Dla większości aplikacji webowych korzyści związane z utrzymaniem i odseparowaniem znacznie przewyższają ten niewielki koszt. JavaScript pozostaje najczęściej używanym językiem w ankiecie Stack Overflow Developer Survey, co podkreśla, jak często deweloperzy mają do czynienia z pracą integracyjną, którą rozwiązują adaptery4.

Najczęściej zadawane pytania

P: Jakiego problemu rozwiązuje wzorzec Adaptera?

O: Rozwiązuje niezgodności interfejsów, tłumacząc wywołania z klienta na wywołania zrozumiałe dla adaptee, dzięki czemu można ponownie użyć kod bez jego zmiany.

P: Jak Adapter pomaga ze starym kodem?

O: Adapter opakowuje stare moduły i udostępnia nowoczesny interfejs, pozwalając zintegrować stary, stabilny kod z nowymi aplikacjami bez ryzykownych przepisów.

P: Kiedy wybrać Adapter zamiast innych wzorców?

O: Wybierz Adapter, gdy potrzebujesz kompatybilności między dwoma niepasującymi interfejsami. Jeśli chcesz uprościć cały podsystem, lepsza będzie Fasada.

Dalsza lektura i linki wewnętrzne


W Clean Code Guy pomagamy zespołom wdrażać praktyczne wzorce projektowe, które przemieniają kruche, złożone bazy kodu w zasoby odporne, testowalne i przyjemne w pracy. Jeśli zmagasz się z systemem legacy lub trudnymi integracjami, nasze Clean Code Audits mogą dać ci jasną, wykonalną mapę drogową do zdrowszej architektury. Dowiedz się, jak możemy pomóc Ci szybciej wypuszczać lepszy kod.

1.
Erich Gamma, Richard Helm, Ralph Johnson i John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). [https://en.wikipedia.org/wiki/Design_Patterns_(book)](https://en.wikipedia.org/wiki/Design_Patterns_(book))
2.
Adapter pattern overview and examples: https://www.geeksforgeeks.org/adapter-pattern/
3.
Node.js documentation for util.promisify, a common approach to convert callbacks to Promises: https://nodejs.org/api/util.html#utilpromisifyoriginal
4.
Stack Overflow Developer Survey 2023, showing the prevalence of JavaScript and web technologies that commonly require integration work: https://survey.stackoverflow.co/2023/
← Back to blog
🙋🏻‍♂️

AI pisze kod.
Ty sprawiasz, że przetrwa.

W erze przyspieszenia AI czysty kod to nie tylko dobra praktyka — to różnica między systemami, które się skalują, a bazami kodu, które zapadają się pod własnym ciężarem.