\section{Dependency Injection}

\begin{frame}{Mi az a Dependency Injection (DI)?}
  \begin{block}{Definíció}
    A \textbf{Dependency Injection} egy tervezési minta, ahol az objektumok nem maguk hozzák létre a függőségeiket, hanem kívülről kapják meg azokat.
  \end{block}
\end{frame}

\begin{frame}{DI előnyei és hátrányai}
  \begin{columns}[T]
    \begin{column}{0.48\textwidth}
      \begin{alertblock}{Nélküle (rossz)}
        \begin{itemize}
          \item Szoros kapcsolás (tight coupling)
          \item Nehéz tesztelni
          \item Nehéz karbantartani
          \item Függőségek rejtettek
        \end{itemize}
      \end{alertblock}
    \end{column}
    \begin{column}{0.48\textwidth}
      \begin{exampleblock}{Vele (jó)}
        \begin{itemize}
          \item Laza kapcsolás (loose coupling)
          \item Könnyű tesztelni
          \item Könnyen karbantartható
          \item Függőségek explicit módon láthatók
        \end{itemize}
      \end{exampleblock}
    \end{column}
  \end{columns}
\end{frame}

\begin{frame}[fragile]{Probléma DI nélkül (1/3)}
  \begin{alertblock}{Szoros kapcsolás példa - Osztály}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor() {
    // A UserService közvetlenül létrehozza 
    // a függőségeit
    this.database = new MySQLDatabase();
    this.emailService = new EmailService();
    this.logger = new Logger();
  }
}
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Probléma DI nélkül (2/3)}
  \begin{alertblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const userService = new UserService();

async createUser(userData) {
  this.logger.log('Creating user...');
  const user = await this.database.save(userData);
  await this.emailService.sendWelcomeEmail(
    user.email
  );
  return user;
}
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Probléma DI nélkül (3/3)}
  \end{alertblock}
  \begin{alertblock}{Problémák}
    \begin{itemize}
      \item Nem lehet könnyen PostgreSQL-re váltani
      \item Teszteléskor valódi email-t küldene
      \item Nem lehet mock objektumokat használni
    \end{itemize}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Megoldás Dependency Injection-nel (1/3)}
  \begin{exampleblock}{Laza kapcsolás DI-val - Konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor(database, emailService, logger) {
    // Függőségek kívülről érkeznek
    this.database = database;
    this.emailService = emailService;
    this.logger = logger;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Megoldás Dependency Injection-nel (2/3)}
  \begin{exampleblock}{createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData) {
    this.logger.log('Creating user...');
    const user = await this.database.save(userData);
    await this.emailService.sendWelcomeEmail(
      user.email);
    return user;
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Megoldás Dependency Injection-nel (3/3)}
  \begin{exampleblock}{Használat - függőségek kívülről}
\small
\begin{lstlisting}[language=JavaScript]
const database = new MySQLDatabase();
const emailService = new EmailService();
const logger = new Logger();

const userService = new UserService(
  database, 
  emailService, 
  logger
);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{DI előnyei}
  \begin{block}{Miért jó a Dependency Injection?}
    \footnotesize
    \begin{enumerate}
      \item \textbf{Tesztelhetőség}
        \begin{itemize}
          \item Mock és stub objektumok használata
          \item Izolált unit tesztek
        \end{itemize}
      \item \textbf{Rugalmasság}
        \begin{itemize}
          \item Könnyen cserélhető implementációk
          \item Különböző konfigurációk más környezetekben
        \end{itemize}
      \item \textbf{Karbantarthatóság}
        \begin{itemize}
          \item Egyértelmű függőségek
          \item Single Responsibility Principle
        \end{itemize}
      \item \textbf{Újrafelhasználhatóság}
        \begin{itemize}
          \item Komponensek függetlenek
          \item Könnyű más projektekben használni
        \end{itemize}
    \end{enumerate}
  \end{block}
\end{frame}

\begin{frame}[fragile]{DI típusok - Constructor Injection (1/2)}
  \begin{block}{Constructor Injection (Ajánlott)}
    Függőségek átadása a konstruktorban
  \end{block}
  
  \begin{exampleblock}{Osztály konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class OrderService {
  constructor(paymentGateway, inventoryService) {
    this.paymentGateway = paymentGateway;
    this.inventoryService = inventoryService;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Constructor Injection (2/2)}
  \begin{exampleblock}{processOrder metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async processOrder(order) {
    await this.inventoryService.reserveItems(
      order.items);
    await this.paymentGateway.charge(order.total);
    return { success: true, orderId: order.id };
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Constructor Injection - Használat}
  \begin{exampleblock}{Dependency injection}
\small
\begin{lstlisting}[language=JavaScript]
const paymentGateway = new StripeGateway();
const inventoryService = new InventoryService();

const orderService = new OrderService(
  paymentGateway, 
  inventoryService
);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Setter Injection (1/3)}
  \begin{block}{Setter Injection}
    Függőségek beállítása setter metódusokon keresztül
  \end{block}
  
  \begin{exampleblock}{Setter metódusok}
\small
\begin{lstlisting}[language=JavaScript]
class ReportService {
  setDatabase(database) {
    this.database = database;
  }
  
  setEmailService(emailService) {
    this.emailService = emailService;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Setter Injection (2/3)}
  \begin{exampleblock}{generateAndSendReport metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async generateAndSendReport() {
    const data = await this.database.fetchData();
    const report = this.createReport(data);
    await this.emailService.send(report);
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Setter Injection (3/3)}
  \begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const reportService = new ReportService();
reportService.setDatabase(new MySQLDatabase());
reportService.setEmailService(new EmailService());
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{Setter Injection - Hátrányok}
  \begin{alertblock}{Hátrány}
    Opcionális függőségek $\rightarrow$ könnyebb hibázni
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Property Injection (1/2)}
  \begin{block}{Property Injection}
    Közvetlen property beállítás (ritkábban használt)
  \end{block}
  
  \begin{exampleblock}{Osztály definíció}
\small
\begin{lstlisting}[language=JavaScript]
class NotificationService {
  async sendNotification(message) {
    if (this.emailSender) {
      await this.emailSender.send(message);
    }
    if (this.smsSender) {
      await this.smsSender.send(message);
    }
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI típusok - Property Injection (2/2)}
  \begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const notificationService = 
  new NotificationService();
  
notificationService.emailSender = 
  new EmailSender();
notificationService.smsSender = 
  new SMSSender();
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{Property Injection - Figyelmeztetés}
  \begin{alertblock}{Figyelem}
    Kevésbé explicit, nehezebb követni a függőségeket
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Dependency Injection TypeScript-tel (1/3)}
  \begin{exampleblock}{Interfészek definiálása}
\small
\begin{lstlisting}[language=JavaScript]
interface IDatabase {
  save(data: any): Promise<any>;
  find(id: string): Promise<any>;
}

interface ILogger {
  log(message: string): void;
  error(message: string): void;
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Dependency Injection TypeScript-tel (2/4)}
  \begin{exampleblock}{Service osztály konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor(
    private database: IDatabase,
    private logger: ILogger
  ) {}
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Dependency Injection TypeScript-tel (3/4)}
  \begin{exampleblock}{createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData: any) {
    this.logger.log('Creating user');
    return await this.database.save(userData);
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Dependency Injection TypeScript-tel (4/4)}
  \begin{exampleblock}{Implementációk és használat}
\small
\begin{lstlisting}[language=JavaScript]
const db: IDatabase = new MongoDatabase();
const logger: ILogger = new ConsoleLogger();
const service = new UserService(db, logger);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (1/4)}
  \begin{exampleblock}{Eredeti service konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class AuthService {
  constructor(database, jwtService) {
    this.database = database;
    this.jwtService = jwtService;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (2/4)}
  \begin{exampleblock}{login metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async login(email, password) {
    const user = await this.database.findByEmail(
      email);
    if (user && user.password === password) {
      return this.jwtService.generateToken(user);
    }
    throw new Error('Invalid credentials');
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (3/4)}
  \begin{exampleblock}{Unit teszt - Mock objektumok (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
test('login with valid credentials', async () => {
  const mockDatabase = {
    findByEmail: jest.fn().mockResolvedValue({
      email: 'test@test.com', 
      password: 'pass123'
    })
  };
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (4/4)}
  \begin{exampleblock}{Unit teszt - Mock objektumok (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
  const mockJwtService = {
    generateToken: jest.fn()
      .mockReturnValue('token123')
  };
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Tesztelés (5/5)}
  \begin{exampleblock}{Unit teszt - Érvényesítés}
\small
\begin{lstlisting}[language=JavaScript]
  const authService = new AuthService(
    mockDatabase, mockJwtService);
  const token = await authService.login(
    'test@test.com', 'pass123');
  
  expect(token).toBe('token123');
  expect(mockDatabase.findByEmail)
    .toHaveBeenCalledWith('test@test.com');
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (1/3)}
  \begin{exampleblock}{Development környezet}
\small
\begin{lstlisting}[language=JavaScript]
if (process.env.NODE_ENV === 'development') {
  const database = new MockDatabase();
  const emailService = new ConsoleEmailService();
  const logger = new VerboseLogger();
  
  app.locals.userService = new UserService(
    database, emailService, logger
  );
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (2/3)}
  \begin{exampleblock}{Production környezet}
\small
\begin{lstlisting}[language=JavaScript]
if (process.env.NODE_ENV === 'production') {
  const database = new PostgreSQLDatabase();
  const emailService = new SendGridEmailService();
  const logger = new CloudLogger();
  
  app.locals.userService = new UserService(
    database, emailService, logger
  );
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI gyakorlatban - Környezeti különbségek (3/3)}
  \begin{exampleblock}{Használat route-okban}
\small
\begin{lstlisting}[language=JavaScript]
app.post('/users', async (req, res) => {
  const user = await app.locals.userService
    .createUser(req.body);
  res.json(user);
});
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Manuális DI - Factory Pattern (1/4)}
  \begin{exampleblock}{Factory függvény - Alapok (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
function createServices(config) {
  // Alapvető szolgáltatások
  const logger = new Logger(config.logLevel);
  const database = new Database(config.dbUrl);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Manuális DI - Factory Pattern (2/4)}
  \begin{exampleblock}{Factory függvény - Alapok (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
  // Összetett szolgáltatások
  const userRepository = 
    new UserRepository(database);
  const emailService = 
    new EmailService(config.emailConfig);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Manuális DI - Factory Pattern (3/5)}
  \begin{exampleblock}{Service-ek létrehozása (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
  const userService = new UserService(
    userRepository, emailService, logger
  );
  
  const authService = new AuthService(
    userRepository, config.jwtSecret, logger
  );
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Manuális DI - Factory Pattern (4/5)}
  \begin{exampleblock}{Service-ek létrehozása (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
  return {
    userService, authService,
    logger, database
  };
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Manuális DI - Factory Pattern (5/5)}
  \begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const config = loadConfig();
const services = createServices(config);

export default services;
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Service Locator Pattern (1/4)}
  \begin{exampleblock}{Service container konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class ServiceContainer {
  constructor() {
    this.services = new Map();
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Service Locator Pattern (2/4)}
  \begin{exampleblock}{register és get metódusok}
\small
\begin{lstlisting}[language=JavaScript]
  register(name, instance) {
    this.services.set(name, instance);
  }
  
  get(name) {
    if (!this.services.has(name)) {
      throw new Error(`Service ${name} not found`);
    }
    return this.services.get(name);
  }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Service Locator Pattern (3/4)}
  \begin{exampleblock}{Service-ek regisztrálása (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
const container = new ServiceContainer();

container.register('database', 
  new MySQLDatabase());
container.register('logger', new Logger());
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Service Locator Pattern (4/4)}
  \begin{exampleblock}{Service-ek regisztrálása (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
container.register('userService', 
  new UserService(
    container.get('database'),
    container.get('logger')
  )
);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{Service Locator Pattern (5/5)}
  \begin{exampleblock}{Használat}
\small
\begin{lstlisting}[language=JavaScript]
const userService = container.get('userService');
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (1/4)}
  \begin{exampleblock}{Service-ek létrehozása}
\small
\begin{lstlisting}[language=JavaScript]
const database = new Database();
const userService = new UserService(database);
const authService = new AuthService(database);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (2/6)}
  \begin{exampleblock}{Middleware factory - Setup}
\small
\begin{lstlisting}[language=JavaScript]
function createAuthMiddleware(authService) {
  return async (req, res, next) => {
    const token = req.headers.authorization
      ?.split(' ')[1];
    if (!token) {
      return res.status(401)
        .json({ error: 'No token' });
    }
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (3/6)}
  \begin{exampleblock}{Middleware factory - Verifikáció}
\small
\begin{lstlisting}[language=JavaScript]
    try {
      req.user = await authService.verifyToken(
        token);
      next();
    } catch (error) {
      res.status(401)
        .json({ error: 'Invalid token' });
    }
  };
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (4/6)}
  \begin{exampleblock}{Route handler factory}
\small
\begin{lstlisting}[language=JavaScript]
function createUserController(userService) {
  return {
    async createUser(req, res) {
      const user = await userService.createUser(
        req.body);
      res.json(user);
    }
  };
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (5/6)}
  \begin{exampleblock}{Setup (1/2)}
\small
\begin{lstlisting}[language=JavaScript]
const authMiddleware = 
  createAuthMiddleware(authService);
const userController = 
  createUserController(userService);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile]{DI az Express middleware-ekben (6/6)}
  \begin{exampleblock}{Setup (2/2)}
\small
\begin{lstlisting}[language=JavaScript]
app.post('/users', 
  authMiddleware, 
  userController.createUser
);
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{SOLID elvek és DI}
  \begin{block}{DI kapcsolata a SOLID elvekkel}
    \begin{description}
      \item[S - Single Responsibility] Osztályok egy dolgot csinálnak, nem hozzák létre saját függőségeiket
      \item[O - Open/Closed] Új implementációk hozzáadhatók a meglévő kód módosítása nélkül
      \item[L - Liskov Substitution] Interfészek lehetővé teszik a helyettesítést
      \item[I - Interface Segregation] Kis, specifikus interfészek használata
      \item[D - Dependency Inversion] \textbf{Függőség absztrakciókra, nem konkrét implementációkra}
    \end{description}
  \end{block}
  
  \begin{alertblock}{Dependency Inversion Principle}
    \begin{itemize}
      \item High-level modulok nem függnek low-level moduloktól
      \item Mindkettő absztrakcióktól (interfészektől) függ
      \item DI ezt a célt szolgálja
    \end{itemize}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Best Practices}
  \begin{block}{Ajánlások}
    \footnotesize
    \begin{enumerate}
      \item \textbf{Constructor Injection előnyben részesítése}
        \begin{itemize}
          \item Kötelező függőségek explicit módon láthatók
          \item Immutábilis objektumok
        \end{itemize}
      \item \textbf{Interfészekre programozás}
        \begin{itemize}
          \item TypeScript interfészek használata
          \item Laza kapcsolás
        \end{itemize}
      \item \textbf{Ne használj Service Locator az üzleti logikában}
        \begin{itemize}
          \item Rejtett függőségek
          \item Csak konfigurációs szinten
        \end{itemize}
      \item \textbf{Kerüld a túl sok függőséget}
        \begin{itemize}
          \item Ha sok függőség van, valószínűleg túl sok felelőssége van az osztálynak
          \item Refaktorálás szükséges
        \end{itemize}
    \end{enumerate}
  \end{block}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (1/3)}
  \begin{alertblock}{Rossz gyakorlat - Konstruktor}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor(container) {
    // Teljes container injektálása
    this.container = container;
  }
}
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (2/3)}
  \begin{alertblock}{Rossz gyakorlat - createUser metódus}
\small
\begin{lstlisting}[language=JavaScript]
  async createUser(userData) {
    // Függőségek rejtettek!
    const db = this.container.get('database');
    const email = this.container.get('emailService');
    const logger = this.container.get('logger');
    logger.log('Creating user');
    const user = await db.save(userData);
    await email.sendWelcome(user.email);
    return user;
  }
\end{lstlisting}
  \end{alertblock}
\end{frame}

\begin{frame}[fragile]{Antipattern - Service Locator visszaélés (3/3)}
  
  \begin{exampleblock}{Jó gyakorlat}
\small
\begin{lstlisting}[language=JavaScript]
class UserService {
  constructor(database, emailService, logger) {
    this.database = database;
    this.emailService = emailService;
    this.logger = logger;
  }
}
\end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}{Összefoglalás (1/2)}
  \begin{block}{Dependency Injection lényege}
    \begin{itemize}
      \item Függőségek kívülről történő átadása
      \item Laza kapcsolás objektumok között
      \item Tesztelhetőség növelése
      \item SOLID elvek támogatása
    \end{itemize}
  \end{block}
  
  \begin{block}{Implementációs módszerek}
    \begin{itemize}
      \item Constructor Injection (preferált)
      \item Setter Injection (opcionális függőségekhez)
      \item Manuális DI factory pattern-nel
      \item Service Container (konfigurációs szinten)
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}{Összefoglalás (2/2)}
  \begin{block}{Előnyök}
    \begin{itemize}
      \item Könnyű tesztelés mock objektumokkal
      \item Rugalmas, cserélhető implementációk
      \item Tisztább, karbantarthatóbb kód
      \item Explicit függőségek
    \end{itemize}
  \end{block}
\end{frame}

