Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Reaktywne wzorce projektowe

W centrum kontroli NOVA LAB kazdy system musi byc niezawodny i przewidywalny. Wzorce projektowe z reaktywnoscia Vue 3 pozwalaja budowac takie systemy - jak sprawdzone protokoly inzynieryjne stacji kosmicznej.

Reactive Singleton

Czasem potrzebujemy jednej wspolnej instancji stanu - jak centralny komputer stacji, z ktorego korzystaja wszystkie moduly.

1import { reactive, readonly } from 'vue'
2
3// Stan tworzony RAZ, poza funkcja
4const globalState = reactive({
5  currentUser: null,
6  theme: 'dark',
7  locale: 'pl',
8  notifications: []
9})
10
11// Composable zwraca ten sam stan za kazdym razem
12export function useGlobalStore() {
13  function setUser(user) {
14    globalState.currentUser = user
15  }
16
17  function setTheme(theme) {
18    globalState.theme = theme
19  }
20
21  function addNotification(msg) {
22    globalState.notifications.push({
23      id: Date.now(),
24      message: msg,
25      read: false
26    })
27  }
28
29  // readonly zapobiega bezposredniej modyfikacji
30  return {
31    state: readonly(globalState),
32    setUser,
33    setTheme,
34    addNotification
35  }
36}
1<!-- Dowolny komponent - ten sam stan -->
2<script setup>
3import { useGlobalStore } from './composables/useGlobalStore'
4
5// Oba komponenty widza te same dane
6const { state, addNotification } = useGlobalStore()
7</script>
8
9<template>
10  <p>Motyw: {{ state.theme }}</p>
11  <p>Powiadomienia: {{ state.notifications.length }}</p>
12</template>

Kluczowa roznica: stan jest zdefiniowany poza funkcja composable, wiec kazde wywolanie

useGlobalStore()
zwraca referencje do tego samego obiektu.

Event Bus z reaktywnoscia

Zamiast klasycznego event busa, mozemy stworzyc reaktywny system zdarzen - jak siec komunikacyjna stacji NOVA LAB.

1import { ref, watch } from 'vue'
2
3function useEventBus() {
4  const lastEvent = ref(null)
5  const listeners = new Map()
6
7  function emit(eventName, payload) {
8    lastEvent.value = { name: eventName, payload, timestamp: Date.now() }
9
10    const handlers = listeners.get(eventName) || []
11    handlers.forEach(handler => handler(payload))
12  }
13
14  function on(eventName, handler) {
15    if (!listeners.has(eventName)) {
16      listeners.set(eventName, [])
17    }
18    listeners.get(eventName).push(handler)
19
20    // Zwroc funkcje unsubscribe
21    return () => {
22      const handlers = listeners.get(eventName)
23      const index = handlers.indexOf(handler)
24      if (index > -1) handlers.splice(index, 1)
25    }
26  }
27
28  return { lastEvent, emit, on }
29}
1<script setup>
2import { onUnmounted } from 'vue'
3
4const { emit, on } = useEventBus()
5
6// Subskrypcja z automatycznym cleanup
7const unsubscribe = on('sensor-alert', (data) => {
8  console.log('Alert z sensora:', data)
9})
10
11onUnmounted(() => {
12  unsubscribe() // Wazne! Zapobiegamy wyciekom pamieci
13})
14
15// Emitowanie zdarzenia
16emit('sensor-alert', { sensorId: 'T-01', value: 45 })
17</script>

State Machine z ref i computed

Automaty stanowe to idealny wzorzec do zarzadzania zlozonym stanem - jak protokoly bezpieczenstwa stacji, gdzie kazdy stan ma dokladnie okreslone przejscia.

1import { ref, computed } from 'vue'
2
3function useStateMachine(config) {
4  const currentState = ref(config.initial)
5
6  const availableTransitions = computed(() => {
7    const stateConfig = config.states[currentState.value]
8    return stateConfig ? Object.keys(stateConfig.on || {}) : []
9  })
10
11  function transition(event) {
12    const stateConfig = config.states[currentState.value]
13    if (!stateConfig || !stateConfig.on || !stateConfig.on[event]) {
14      console.warn(
15        `Niedozwolone przejscie: ${currentState.value} -> ${event}`
16      )
17      return false
18    }
19
20    const nextState = stateConfig.on[event]
21    currentState.value = nextState
22    return true
23  }
24
25  function canTransition(event) {
26    return availableTransitions.value.includes(event)
27  }
28
29  return { currentState, availableTransitions, transition, canTransition }
30}
1<script setup>
2const experimentMachine = useStateMachine({
3  initial: 'idle',
4  states: {
5    idle: {
6      on: { START: 'running', CONFIGURE: 'configuring' }
7    },
8    configuring: {
9      on: { SAVE: 'idle', CANCEL: 'idle' }
10    },
11    running: {
12      on: { PAUSE: 'paused', COMPLETE: 'completed', FAIL: 'failed' }
13    },
14    paused: {
15      on: { RESUME: 'running', ABORT: 'failed' }
16    },
17    completed: {
18      on: { RESET: 'idle' }
19    },
20    failed: {
21      on: { RESET: 'idle', RETRY: 'running' }
22    }
23  }
24})
25
26// Uzywamy w template
27// experimentMachine.currentState.value => 'idle'
28// experimentMachine.transition('START') => stan zmienia sie na 'running'
29// experimentMachine.canTransition('PAUSE') => true
30</script>

Reactive Observer Pattern

Wzorzec obserwatora z reaktywnoscia Vue - jak system monitorowania, gdzie kazdy sensor moze miec wielu obserwatorow.

1import { ref, watch } from 'vue'
2
3function useObservable(initialValue) {
4  const value = ref(initialValue)
5  const history = ref([])
6  const observers = ref(0)
7
8  // Kazdy watch to nowy obserwator
9  function subscribe(callback) {
10    observers.value++
11    const unwatch = watch(value, (newVal, oldVal) => {
12      callback({ newVal, oldVal, timestamp: Date.now() })
13    })
14
15    return () => {
16      unwatch()
17      observers.value--
18    }
19  }
20
21  function setValue(newValue) {
22    history.value.push({
23      from: value.value,
24      to: newValue,
25      timestamp: Date.now()
26    })
27    if (history.value.length > 100) history.value.shift()
28    value.value = newValue
29  }
30
31  return {
32    value: readonly(value),
33    history: readonly(history),
34    observers,
35    subscribe,
36    setValue
37  }
38}
1<script setup>
2import { readonly, onUnmounted } from 'vue'
3
4const temperature = useObservable(22)
5
6// Obserwator 1 - alarm
7const unsub1 = temperature.subscribe(({ newVal }) => {
8  if (newVal > 40) console.warn('Temperatura krytyczna!')
9})
10
11// Obserwator 2 - logowanie
12const unsub2 = temperature.subscribe(({ newVal, oldVal }) => {
13  console.log(`Temp: ${oldVal} -> ${newVal}`)
14})
15
16onUnmounted(() => {
17  unsub1()
18  unsub2()
19})
20
21// Zmiana wartosci powiadamia wszystkich obserwatorow
22temperature.setValue(25)
23</script>
24
25<template>
26  <p>Temperatura: {{ temperature.value }}°C</p>
27  <p>Aktywni obserwatorzy: {{ temperature.observers }}</p>
28  <p>Historia zmian: {{ temperature.history.length }}</p>
29</template>
Ir a CodeWorlds