Sobes.tech
Middle+

Πες μου για το σχεδιασμό προτύπων που χρησιμοποίησες στην ανάπτυξή σου.

sobes.tech AI

Απάντηση από AI

Έχω χρησιμοποιήσει τα ακόλουθα πρότυπα σχεδίασης:

  • MVC (Model-View-Controller): Διαχωρισμός της λογικής της εφαρμογής. Το μοντέλο διαχειρίζεται τα δεδομένα, η προβολή είναι υπεύθυνη για την εμφάνιση, και ο ελεγκτής χειρίζεται την αλληλεπίδραση του χρήστη και ενημερώνει το μοντέλο ή την προβολή. Το χρησιμοποίησα στο Angular, όπου τα components υλοποιούν μερικώς αυτή τη φιλοσοφία.

  • MVVM (Model-View-ViewModel): Παρόμοιο με το MVC, αλλά εισάγει το ViewModel ως μεσάζοντα μεταξύ της προβολής και του μοντέλου. Το ViewModel περιέχει επιχειρηματική λογική για την εμφάνιση δεδομένων και την επεξεργασία ενεργειών χρήστη. Το χρησιμοποίησα ενεργά με React και Vuex (Vue), όπου η κατάσταση της εφαρμογής διαχειρίζεται κεντρικά.

    // Παράδειγμα αντιδραστικότητας στο React με χρήση useState
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // το useState είναι μέρος του μηχανισμού MVVM
    
      const increment = () => {
        setCount(count + 1); // ενημέρωση κατάστασης μέσω του setter
      };
    
      return (
        <div>
          <p>Μετρητής: {count}</p> {/* εμφάνιση κατάστασης */}
          <button onClick={increment}>Αύξηση</button> {/* ενέργεια που επηρεάζει την κατάσταση */}
        </div>
      );
    }
    
  • Σχέδιο συστατικών (Component Pattern): Διαχωρισμός του περιβάλλοντος χρήστη σε ανεξάρτητα, επαναχρησιμοποιήσιμα συστατικά. Κάθε συστατικό ενσωματώνει τη λογική, τη δομή και τα στυλ του. Βάση των σύγχρονων πλαισίων όπως React, Vue και Angular.

    // Παράδειγμα απλού συστατικού React
    function Button(props) {
      return (
        <button className="my-button" onClick={props.onClick}>
          {props.label}
        </button>
      );
    }
    
  • Προσαρμογέας (Adapter): Μετατρέπει το interface μιας κλάσης σε άλλο που αναμένουν οι πελάτες. Επιτρέπει τη συνεργασία κλάσεων με ασυμβίβαστα interfaces. Το χρησιμοποίησα για ενσωμάτωση τρίτων βιβλιοθηκών ή API με διαφορετικές υπογραφές μεθόδων.

    // Παράδειγμα προσαρμογέα για διαφορετικούς πελάτες API
    class OldApi {
      getDataOld(id) {
        console.log(`Λήψη δεδομένων από το Old API για το id ${id}`);
        return { id: id, name: `Item ${id}` };
      }
    }
    
    class NewApi {
      fetchDataNew(itemId) {
        console.log(`Λήψη δεδομένων από το New API για το item id ${itemId}`);
        return { itemId: itemId, label: `Product ${itemId}` };
      }
    }
    
    class ApiAdapter {
      constructor(apiClient) {
        this.apiClient = apiClient;
      }
    
      getData(id) {
        if (this.apiClient instanceof OldApi) {
          return this.apiClient.getDataOld(id);
        } else if (this.apiClient instanceof NewApi) {
          // Μετατροπή δεδομένων από το νέο φορμάτ
          const newData = this.apiClient.fetchDataNew(id);
          return { id: newData.itemId, name: newData.label };
        }
        throw new Error("Αντιμετωπίζω μη υποστηριζόμενο πελάτη API");
      }
    }
    
    const oldApiAdapter = new ApiAdapter(new OldApi());
    console.log(oldApiAdapter.getData(1));
    
    const newApiAdapter = new ApiAdapter(new NewApi());
    console.log(newApiAdapter.getData(2));
    
  • Δεικτοδότης (Iterator): Παρέχει τρόπο διαδοχικής πρόσβασης σε στοιχεία ενός σύνθετου αντικειμένου, χωρίς να αποκαλύπτει την εσωτερική του αναπαράσταση. Χρησιμοποιείται σε βρόχους (for...of) και μεθόδους πινάκων (forEach, map, filter).

    // Παράδειγμα χρήσης δείκτη σε JavaScript
    const numbers = [1, 2, 3, 4, 5];
    
    // Ρητή απόκτηση δείκτη
    const iterator = numbers[Symbol.iterator]();
    
    console.log(iterator.next()); // { value: 1, done: false }
    console.log(iterator.next()); // { value: 2, done: false }
    // ...
    console.log(iterator.next()); // { value: undefined, done: true }
    
    // Χρήση δείκτη σε for...of
    for (const number of numbers) {
      console.log(number);
    }
    
  • Διακοσμητής (Decorator): Προσθέτει δυναμικά νέες ευθύνες σε ένα αντικείμενο, επιτρέποντας την επέκταση της λειτουργικότητας του χωρίς να αλλάζει η δομή του. Το χρησιμοποίησα ως HOC (Higher-Order Components) στο React ή διακοσμητές στο Angular/TypeScript.

    // Παράδειγμα HOC στο React
    function withLogging(WrappedComponent) {
      return function WithLogging(props) {
        console.log(`Το συστατικό ${WrappedComponent.name} αποδόθηκε με τα props:`, props);
        return <WrappedComponent {...props} />;
      };
    }
    
    // Χρήση HOC
    const EnhancedButton = withLogging(Button);
    
    // Χρήση του βελτιωμένου συστατικού
    // <EnhancedButton label="Click me" onClick={() => console.log('Κλικ!')} />
    
  • Μοναδικό (Singleton): Διασφαλίζει ότι μια κλάση έχει μόνο ένα παράδειγμα και παρέχει παγκόσμια πρόσβαση σε αυτό. Το χρησιμοποίησα για διαχείριση παγκόσμιας κατάστασης ή για δημιουργία ενιαίου παραδείγματος υπηρεσίας (π.χ. υπηρεσία καταγραφής, διαμόρφωσης).

    // Παράδειγμα μοτίβου Μοναδικού
    class ConfigManager {
      constructor() {
        if (ConfigManager.instance) {
          return ConfigManager.instance;
        }
        this.settings = {}; // Αρχικοποίηση ρυθμίσεων
        ConfigManager.instance = this;
      }
    
      setSetting(key, value) {
        this.settings[key] = value;
      }
    
      getSetting(key) {
        return this.settings[key];
      }
    }
    
    const config1 = new ConfigManager();
    const config2 = new ConfigManager();
    
    console.log(config1 === config2); // true (το ίδιο παράδειγμα)
    
    config1.setSetting('apiUrl', 'https://api.example.com');
    console.log(config2.getSetting('apiUrl')); // https://api.example.com
    
  • Μέθοδος Εργοστασίου (Factory Method): Ορίζει το interface για τη δημιουργία ενός αντικειμένου, αλλά επιτρέπει στις υποκλάσεις να αποφασίζουν ποιο αντικείμενο να δημιουργήσουν. Το χρησιμοποίησα για τη δημιουργία διαφόρων τύπων στοιχείων χρήστη ή αντικειμένων βάσει διαμόρφωσης.

  • Στρατηγική (Strategy): Ορίζει μια οικογένεια αλγορίθμων, encapsulates κάθε έναν και τα καθιστά εναλλάξιμα. Επιτρέπει στον αλγόριθμο να αλλάζει ανεξάρτητα από τους πελάτες που τον χρησιμοποιούν. Το χρησιμοποίησα για την υλοποίηση διαφορετικών προσεγγίσεων ταξινόμησης δεδομένων, επικύρωσης φορμών ή επεξεργασίας συμβάντων.

Η χρήση αυτών των προτύπων βοήθησε στη δημιουργία πιο δομημένου, modular, δοκιμασμένου και εύκολα συντηρήσιμου κώδικα.