From 3558207f63e4dd3f8cc06f82c0b8dbc5235c6013 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 06:02:26 +0000 Subject: [PATCH 1/2] Initial plan From dc556d96c4f0b9cdd30cb1b10676c08e2c3d547a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Sep 2025 06:12:09 +0000 Subject: [PATCH 2/2] Complete DevPatterns folder structure with comprehensive pattern organization Co-authored-by: ctrl-hack <26658820+ctrl-hack@users.noreply.github.com> --- .gitignore | 50 ++ README.md | 128 ++++- modern-web-patterns/README.md | 163 +++++++ package.json | 29 ++ react-patterns/README.md | 105 ++++ .../hooks-pattern/README.md | 461 ++++++++++++++++++ vanilla-patterns/README.md | 71 +++ .../design-patterns/observer/README.md | 303 ++++++++++++ .../design-patterns/observer/index.js | 237 +++++++++ .../design-patterns/singleton/README.md | 208 ++++++++ .../design-patterns/singleton/example/demo.js | 191 ++++++++ .../design-patterns/singleton/index.js | 122 +++++ 12 files changed, 2067 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 modern-web-patterns/README.md create mode 100644 package.json create mode 100644 react-patterns/README.md create mode 100644 react-patterns/component-patterns/hooks-pattern/README.md create mode 100644 vanilla-patterns/README.md create mode 100644 vanilla-patterns/design-patterns/observer/README.md create mode 100644 vanilla-patterns/design-patterns/observer/index.js create mode 100644 vanilla-patterns/design-patterns/singleton/README.md create mode 100644 vanilla-patterns/design-patterns/singleton/example/demo.js create mode 100644 vanilla-patterns/design-patterns/singleton/index.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ec6737 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +yarn.lock + +# Build outputs +/dist +/build +/.next +/.nuxt +/coverage + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +logs +*.log + +# Temporary files +/tmp +*.tmp +*.temp + +# Cache directories +.cache/ +.parcel-cache/ \ No newline at end of file diff --git a/README.md b/README.md index 5645bb4..ce8cdef 100644 --- a/README.md +++ b/README.md @@ -1 +1,127 @@ -# DevPatterns \ No newline at end of file +# DevPatterns + +A comprehensive collection of development patterns inspired by [patterns.dev](https://www.patterns.dev/), organized from vanilla JavaScript fundamentals to advanced React patterns. + +## ๐Ÿ“š Pattern Categories + +### ๐ŸŸจ Vanilla Patterns +Start your journey with fundamental patterns that work in any JavaScript environment. + +#### ๐ŸŽฏ Design Patterns +Classic software design patterns implemented in vanilla JavaScript: +- **Creational**: [Singleton](./vanilla-patterns/design-patterns/singleton), [Factory](./vanilla-patterns/design-patterns/factory), [Abstract Factory](./vanilla-patterns/design-patterns/abstract-factory), [Builder](./vanilla-patterns/design-patterns/builder), [Prototype](./vanilla-patterns/design-patterns/prototype) +- **Structural**: [Adapter](./vanilla-patterns/design-patterns/adapter), [Bridge](./vanilla-patterns/design-patterns/bridge), [Composite](./vanilla-patterns/design-patterns/composite), [Decorator](./vanilla-patterns/design-patterns/decorator), [Facade](./vanilla-patterns/design-patterns/facade), [Flyweight](./vanilla-patterns/design-patterns/flyweight), [Proxy](./vanilla-patterns/design-patterns/proxy) +- **Behavioral**: [Chain of Responsibility](./vanilla-patterns/design-patterns/chain-of-responsibility), [Command](./vanilla-patterns/design-patterns/command), [Iterator](./vanilla-patterns/design-patterns/iterator), [Mediator](./vanilla-patterns/design-patterns/mediator), [Memento](./vanilla-patterns/design-patterns/memento), [Observer](./vanilla-patterns/design-patterns/observer), [State](./vanilla-patterns/design-patterns/state), [Strategy](./vanilla-patterns/design-patterns/strategy), [Template Method](./vanilla-patterns/design-patterns/template-method), [Visitor](./vanilla-patterns/design-patterns/visitor) + +#### โšก Performance Patterns +Optimize your vanilla JavaScript applications: +- [Bundling](./vanilla-patterns/performance-patterns/bundling) +- [Code Splitting](./vanilla-patterns/performance-patterns/code-splitting) +- [Tree Shaking](./vanilla-patterns/performance-patterns/tree-shaking) +- [Preload](./vanilla-patterns/performance-patterns/preload) & [Prefetch](./vanilla-patterns/performance-patterns/prefetch) +- [Import on Visibility](./vanilla-patterns/performance-patterns/import-on-visibility) +- [Import on Interaction](./vanilla-patterns/performance-patterns/import-on-interaction) +- [Route-based Splitting](./vanilla-patterns/performance-patterns/route-based-splitting) +- [Bundle Splitting](./vanilla-patterns/performance-patterns/bundle-splitting) + +### โš›๏ธ React Patterns +Advanced patterns specifically for React applications. + +#### ๐Ÿงฉ Component Patterns +Master React component design and composition: +- [Container/Presentational](./react-patterns/component-patterns/container-presentational) +- [Higher-Order Components](./react-patterns/component-patterns/higher-order-component) +- [Render Props](./react-patterns/component-patterns/render-props) +- [Hooks Pattern](./react-patterns/component-patterns/hooks-pattern) +- [Provider Pattern](./react-patterns/component-patterns/provider-pattern) +- [Compound Pattern](./react-patterns/component-patterns/compound-pattern) + +#### ๐Ÿ–ฅ๏ธ Rendering Patterns +Optimize how and when your React components render: +- [Client-Side Rendering](./react-patterns/rendering-patterns/client-side-rendering) +- [Server-Side Rendering](./react-patterns/rendering-patterns/server-side-rendering) +- [Static Generation](./react-patterns/rendering-patterns/static-generation) +- [Incremental Static Generation](./react-patterns/rendering-patterns/incremental-static-generation) +- [Progressive Hydration](./react-patterns/rendering-patterns/progressive-hydration) +- [Streaming SSR](./react-patterns/rendering-patterns/streaming-ssr) +- [Selective Hydration](./react-patterns/rendering-patterns/selective-hydration) +- [Islands Architecture](./react-patterns/rendering-patterns/islands-architecture) + +#### ๐Ÿš€ Performance Patterns +React-specific performance optimizations: +- [Dynamic Imports](./react-patterns/performance-patterns/dynamic-imports) +- [React.lazy](./react-patterns/performance-patterns/react-lazy) +- [Code Splitting](./react-patterns/performance-patterns/code-splitting) +- [Bundle Splitting](./react-patterns/performance-patterns/bundle-splitting) +- [Tree Shaking](./react-patterns/performance-patterns/tree-shaking) +- [Virtualization](./react-patterns/performance-patterns/virtualization) +- [Memoization](./react-patterns/performance-patterns/memoization) + +### ๐ŸŒ Modern Web Patterns +Cutting-edge patterns for modern web applications: +- [Micro Frontends](./modern-web-patterns/micro-frontends) +- [Module Federation](./modern-web-patterns/module-federation) +- [Web Workers](./modern-web-patterns/web-workers) +- [Service Workers](./modern-web-patterns/service-workers) +- [WebAssembly](./modern-web-patterns/web-assembly) +- [Streaming](./modern-web-patterns/streaming) +- [Progressive Web Apps](./modern-web-patterns/progressive-web-apps) + +## ๐Ÿš€ Getting Started + +1. **Clone the repository**: + ```bash + git clone https://github.com/ctrl-hack/DevPatterns.git + cd DevPatterns + ``` + +2. **Install dependencies**: + ```bash + npm install + ``` + +3. **Choose your starting point**: + - **New to patterns?** Start with [vanilla-patterns/design-patterns/singleton](./vanilla-patterns/design-patterns/singleton) + - **React developer?** Jump to [react-patterns/component-patterns](./react-patterns/component-patterns) + - **Performance focused?** Explore performance patterns in both vanilla and React sections + +## ๐Ÿ“– Learning Path + +### Recommended Learning Sequence: + +1. **Foundation** (Vanilla Patterns) + - Start with Design Patterns (Singleton โ†’ Observer โ†’ Factory) + - Learn Performance Patterns basics + +2. **React Fundamentals** + - Component Patterns (Container/Presentational โ†’ Hooks) + - Basic Rendering Patterns (CSR โ†’ SSR) + +3. **Advanced React** + - Advanced Component Patterns (HOC โ†’ Render Props) + - Advanced Rendering Patterns (SSG โ†’ Islands) + - React Performance Patterns + +4. **Modern Architecture** + - Modern Web Patterns + - Advanced Performance Optimization + +## ๐Ÿค Contributing + +Each pattern directory contains: +- `README.md` - Pattern explanation and use cases +- `index.js` - Basic implementation +- `example/` - Practical examples +- `tests/` - Unit tests (where applicable) + +Feel free to contribute implementations, examples, or improvements! + +## ๐Ÿ“š Resources + +- [Patterns.dev](https://www.patterns.dev/) - The inspiration for this repository +- [JavaScript Design Patterns](https://addyosmani.com/resources/essentialjsdesignpatterns/book/) +- [React Patterns](https://reactpatterns.com/) + +## ๐Ÿ“„ License + +MIT License - see [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/modern-web-patterns/README.md b/modern-web-patterns/README.md new file mode 100644 index 0000000..41fc54d --- /dev/null +++ b/modern-web-patterns/README.md @@ -0,0 +1,163 @@ +# Modern Web Patterns + +Advanced patterns for building cutting-edge web applications using the latest technologies and architectural approaches. + +## ๐Ÿ—๏ธ Architecture Patterns + +### Micro Frontends +- **[Micro Frontends](./micro-frontends)** - Break large frontend applications into smaller, manageable pieces +- **[Module Federation](./module-federation)** - Share code between separate applications at runtime + +### Benefits: +- Independent development and deployment +- Technology diversity across teams +- Better scalability and maintainability +- Fault isolation + +## ๐Ÿงต Concurrency Patterns + +### Web Workers +- **[Web Workers](./web-workers)** - Run JavaScript in background threads +- **[Service Workers](./service-workers)** - Enable offline functionality and advanced caching + +### Use Cases: +- Heavy computations without blocking UI +- Background data processing +- Offline-first applications +- Push notifications +- Advanced caching strategies + +## ๐Ÿš€ Performance Patterns + +### Advanced Loading +- **[Streaming](./streaming)** - Stream content to users as it becomes available +- **[Progressive Web Apps](./progressive-web-apps)** - Build app-like experiences on the web + +### Modern Technologies +- **[WebAssembly](./web-assembly)** - Run high-performance code in the browser + +## When to Use These Patterns + +### Micro Frontends +โœ… **Use when:** +- Large teams working on different parts +- Need technology diversity +- Independent deployment cycles +- Legacy system integration + +โŒ **Avoid when:** +- Small applications +- Simple requirements +- Performance is critical +- Limited development resources + +### Web Workers +โœ… **Use when:** +- CPU-intensive tasks +- Large data processing +- Complex calculations +- Image/video manipulation + +โŒ **Avoid when:** +- Simple DOM manipulations +- Small calculations +- Frequent data exchange needed +- Simple applications + +### Service Workers +โœ… **Use when:** +- Offline functionality needed +- Advanced caching required +- Push notifications +- Background sync + +โŒ **Avoid when:** +- Simple static sites +- No offline requirements +- Limited browser support needed + +### WebAssembly +โœ… **Use when:** +- Performance-critical code +- Porting existing C/C++/Rust code +- Complex mathematical operations +- Games or simulations + +โŒ **Avoid when:** +- Simple web applications +- Heavy DOM manipulation +- Small performance gains +- First-time WASM developers + +## Implementation Complexity + +| Pattern | Complexity | Setup Time | Maintenance | +|---------|------------|------------|-------------| +| Micro Frontends | High | Weeks | High | +| Module Federation | Medium | Days | Medium | +| Web Workers | Medium | Hours | Low | +| Service Workers | Medium | Days | Medium | +| WebAssembly | High | Days-Weeks | Medium | +| Streaming | Medium | Days | Medium | +| PWA | Medium | Days | Medium | + +## Prerequisites + +### Technical Knowledge +- Strong JavaScript fundamentals +- Understanding of build tools and bundlers +- Basic knowledge of HTTP and web protocols +- Browser API familiarity + +### For Micro Frontends +- Module bundlers (webpack, Rollup, Vite) +- Build and deployment pipelines +- Container orchestration (optional) + +### For Workers +- Understanding of JavaScript concurrency +- Browser security models +- Async/await and Promises + +### For WebAssembly +- Basic knowledge of C/C++/Rust (optional) +- Understanding of memory management +- Build toolchains (Emscripten, wasm-pack) + +## Getting Started + +1. **Begin with Web Workers** - Easiest to implement and understand +2. **Try Service Workers** - Add offline capabilities to existing apps +3. **Explore Streaming** - Improve perceived performance +4. **Consider PWA features** - Enhance user experience +5. **Evaluate Micro Frontends** - For larger applications +6. **Experiment with WebAssembly** - For performance-critical features + +## Browser Support + +Most modern patterns require modern browsers. Check compatibility: + +- **Web Workers**: Excellent (95%+ support) +- **Service Workers**: Good (90%+ support) +- **Module Federation**: Requires modern bundlers +- **WebAssembly**: Excellent (95%+ support) +- **Streaming**: Good with polyfills +- **PWA Features**: Varies by feature + +## Performance Considerations + +These patterns can significantly improve or hurt performance depending on implementation: + +### Potential Benefits +- Faster loading through better caching +- Non-blocking operations with workers +- Reduced main thread work +- Better resource utilization + +### Potential Drawbacks +- Additional complexity overhead +- Network latency in micro frontends +- Memory usage with multiple contexts +- Build and deployment complexity + +Choose patterns based on actual performance requirements and measurement data. \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..70ffa39 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "dev-patterns", + "version": "1.0.0", + "description": "A comprehensive collection of development patterns from vanilla JavaScript to React patterns", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "echo \"Development server not configured yet\"", + "build": "echo \"Build script not configured yet\"" + }, + "keywords": [ + "design-patterns", + "javascript", + "react", + "performance", + "web-development", + "patterns", + "architecture" + ], + "author": "DevPatterns Contributors", + "license": "MIT", + "dependencies": {}, + "devDependencies": {}, + "repository": { + "type": "git", + "url": "https://github.com/ctrl-hack/DevPatterns.git" + }, + "homepage": "https://github.com/ctrl-hack/DevPatterns#readme" +} \ No newline at end of file diff --git a/react-patterns/README.md b/react-patterns/README.md new file mode 100644 index 0000000..bb6d1f6 --- /dev/null +++ b/react-patterns/README.md @@ -0,0 +1,105 @@ +# React Patterns + +This section covers patterns specific to React applications, from component design to rendering strategies and performance optimizations. + +## ๐Ÿงฉ Component Patterns + +Learn how to structure and compose React components effectively. + +### Core Patterns + +- **[Container/Presentational](./component-patterns/container-presentational)** - Separate data logic from presentation +- **[Higher-Order Components](./component-patterns/higher-order-component)** - Enhance components with additional functionality +- **[Render Props](./component-patterns/render-props)** - Share code between components using props +- **[Hooks Pattern](./component-patterns/hooks-pattern)** - Use state and lifecycle features in functional components +- **[Provider Pattern](./component-patterns/provider-pattern)** - Share data across component tree +- **[Compound Pattern](./component-patterns/compound-pattern)** - Create flexible, composable component APIs + +### When to Use Each Pattern + +- **Container/Presentational**: When you want to separate concerns and make components more reusable +- **HOCs**: When you need to enhance multiple components with similar functionality +- **Render Props**: When you need flexible, configurable component behavior +- **Hooks**: Modern React development (preferred for new applications) +- **Provider**: When you need to share state across multiple components +- **Compound**: When building complex UI components with multiple parts + +## ๐Ÿ–ฅ๏ธ Rendering Patterns + +Optimize how and when your React components render for better performance and user experience. + +### Client-Side Patterns + +- **[Client-Side Rendering](./rendering-patterns/client-side-rendering)** - Render components in the browser +- **[Progressive Hydration](./rendering-patterns/progressive-hydration)** - Hydrate components progressively +- **[Selective Hydration](./rendering-patterns/selective-hydration)** - Hydrate only necessary components + +### Server-Side Patterns + +- **[Server-Side Rendering](./rendering-patterns/server-side-rendering)** - Render on the server for faster initial loads +- **[Static Generation](./rendering-patterns/static-generation)** - Pre-build pages at build time +- **[Incremental Static Generation](./rendering-patterns/incremental-static-generation)** - Update static pages on demand +- **[Streaming SSR](./rendering-patterns/streaming-ssr)** - Stream HTML as it's generated + +### Modern Patterns + +- **[Islands Architecture](./rendering-patterns/islands-architecture)** - Combine static and dynamic content efficiently + +## ๐Ÿš€ Performance Patterns + +React-specific optimizations to make your applications faster and more efficient. + +### Code Loading Patterns + +- **[Dynamic Imports](./performance-patterns/dynamic-imports)** - Load code on demand +- **[React.lazy](./performance-patterns/react-lazy)** - Lazy load React components +- **[Code Splitting](./performance-patterns/code-splitting)** - Split your app into smaller chunks + +### Optimization Patterns + +- **[Memoization](./performance-patterns/memoization)** - Prevent unnecessary re-renders with React.memo, useMemo, useCallback +- **[Virtualization](./performance-patterns/virtualization)** - Efficiently render large lists +- **[Tree Shaking](./performance-patterns/tree-shaking)** - Remove unused React code +- **[Bundle Splitting](./performance-patterns/bundle-splitting)** - Optimize how your React app is bundled + +## Learning Path + +### Beginner +1. Start with **Container/Presentational** pattern +2. Learn basic **Hooks Pattern** +3. Understand **Client-Side Rendering** +4. Try **React.lazy** for code splitting + +### Intermediate +1. Master **Provider Pattern** for state management +2. Learn **Server-Side Rendering** +3. Implement **Memoization** techniques +4. Explore **Higher-Order Components** + +### Advanced +1. **Render Props** for advanced component composition +2. **Progressive Hydration** for performance +3. **Islands Architecture** for modern apps +4. **Compound Pattern** for complex UI components + +## Prerequisites + +- Basic React knowledge (components, props, state) +- Understanding of JavaScript ES6+ features +- Familiarity with React Hooks (for modern patterns) +- Basic understanding of build tools (webpack, Vite, etc.) + +## Pattern Selection Guide + +| Need | Recommended Pattern | +|------|-------------------| +| Separate logic from UI | Container/Presentational | +| Share functionality across components | HOC or Custom Hooks | +| Flexible component behavior | Render Props | +| Modern React development | Hooks Pattern | +| Global state management | Provider Pattern | +| Complex component APIs | Compound Pattern | +| SEO-friendly pages | SSR or Static Generation | +| Fast initial loads | SSR with Progressive Hydration | +| Large lists | Virtualization | +| Reduce bundle size | Code Splitting + Tree Shaking | \ No newline at end of file diff --git a/react-patterns/component-patterns/hooks-pattern/README.md b/react-patterns/component-patterns/hooks-pattern/README.md new file mode 100644 index 0000000..71e2d1a --- /dev/null +++ b/react-patterns/component-patterns/hooks-pattern/README.md @@ -0,0 +1,461 @@ +# Hooks Pattern + +React Hooks allow you to use state and other React features in functional components, providing a more direct API to the React concepts you already know. + +## Problem + +Before Hooks, you needed class components to: +- Use state +- Use lifecycle methods +- Share logic between components +- Access React features + +This led to: +- Verbose class syntax +- Confusing `this` binding +- Difficult component logic reuse +- Complex component hierarchies + +## Solution + +Hooks provide a way to: +- Use state in functional components +- Share logic between components +- Access all React features +- Create cleaner, more readable code + +## Built-in Hooks + +### useState +Manage state in functional components: + +```javascript +import React, { useState } from 'react'; + +function Counter() { + const [count, setCount] = useState(0); + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +### useEffect +Handle side effects and lifecycle events: + +```javascript +import React, { useState, useEffect } from 'react'; + +function UserProfile({ userId }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchUser() { + setLoading(true); + try { + const response = await fetch(`/api/users/${userId}`); + const userData = await response.json(); + setUser(userData); + } catch (error) { + console.error('Failed to fetch user:', error); + } finally { + setLoading(false); + } + } + + fetchUser(); + }, [userId]); // Dependency array + + if (loading) return
Loading...
; + if (!user) return
User not found
; + + return ( +
+

{user.name}

+

{user.email}

+
+ ); +} +``` + +### useContext +Access context values without nested consumers: + +```javascript +import React, { useContext } from 'react'; + +const ThemeContext = React.createContext(); + +function Button() { + const theme = useContext(ThemeContext); + + return ( + + ); +} + +function App() { + const theme = { bg: 'blue', color: 'white' }; + + return ( + + + + + + ); +} +``` + +### useFetch Hook +```javascript +import { useState, useEffect } from 'react'; + +function useFetch(url) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const abortController = new AbortController(); + + async function fetchData() { + try { + setLoading(true); + setError(null); + + const response = await fetch(url, { + signal: abortController.signal + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + setData(result); + } catch (err) { + if (err.name !== 'AbortError') { + setError(err.message); + } + } finally { + setLoading(false); + } + } + + fetchData(); + + return () => abortController.abort(); + }, [url]); + + return { data, loading, error }; +} + +// Usage +function UserList() { + const { data: users, loading, error } = useFetch('/api/users'); + + if (loading) return
Loading users...
; + if (error) return
Error: {error}
; + + return ( + + ); +} +``` + +### useLocalStorage Hook +```javascript +import { useState, useEffect } from 'react'; + +function useLocalStorage(key, initialValue) { + const [storedValue, setStoredValue] = useState(() => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch (error) { + console.error(`Error reading localStorage key "${key}":`, error); + return initialValue; + } + }); + + const setValue = (value) => { + try { + const valueToStore = value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } catch (error) { + console.error(`Error setting localStorage key "${key}":`, error); + } + }; + + return [storedValue, setValue]; +} + +// Usage +function Settings() { + const [theme, setTheme] = useLocalStorage('theme', 'light'); + + return ( +
+

Current theme: {theme}

+ +
+ ); +} +``` + +## Hook Rules + +1. **Only call Hooks at the top level** - Don't call Hooks inside loops, conditions, or nested functions +2. **Only call Hooks from React functions** - Call from React function components or custom Hooks + +```javascript +// โŒ Wrong - conditional hook +function WrongComponent({ condition }) { + if (condition) { + const [count, setCount] = useState(0); // Don't do this! + } +} + +// โœ… Correct - hook at top level +function CorrectComponent({ condition }) { + const [count, setCount] = useState(0); + + if (condition) { + // Use the hook result conditionally + return
Count: {count}
; + } + + return
No condition
; +} +``` + +## Advanced Patterns + +### useReducer for Complex State +```javascript +import React, { useReducer } from 'react'; + +const initialState = { count: 0, step: 1 }; + +function reducer(state, action) { + switch (action.type) { + case 'increment': + return { ...state, count: state.count + state.step }; + case 'decrement': + return { ...state, count: state.count - state.step }; + case 'setStep': + return { ...state, step: action.step }; + case 'reset': + return initialState; + default: + throw new Error('Unknown action type'); + } +} + +function Counter() { + const [state, dispatch] = useReducer(reducer, initialState); + + return ( +
+

Count: {state.count}

+

Step: {state.step}

+ dispatch({ type: 'setStep', step: Number(e.target.value) })} + /> + + + +
+ ); +} +``` + +### useCallback and useMemo for Performance +```javascript +import React, { useState, useMemo, useCallback } from 'react'; + +function ExpensiveComponent({ items, filter }) { + const [count, setCount] = useState(0); + + // Memoize expensive calculation + const filteredItems = useMemo(() => { + console.log('Filtering items...'); + return items.filter(item => item.name.includes(filter)); + }, [items, filter]); + + // Memoize callback to prevent child re-renders + const handleClick = useCallback(() => { + setCount(c => c + 1); + }, []); + + return ( +
+

Count: {count}

+ + +
+ ); +} +``` + +## Use Cases + +### โœ… Good Use Cases +- **State management** in functional components +- **Side effects** (API calls, subscriptions, DOM manipulation) +- **Logic reuse** across components +- **Context consumption** without render props +- **Performance optimization** with memoization + +### โŒ Avoid When +- Class components work better for your use case +- You need complex lifecycle methods (rare) +- Team is not familiar with functional programming concepts + +## Pros and Cons + +### Pros โœ… +- Simpler, more readable code +- Better logic reuse with custom hooks +- No `this` binding confusion +- Easier to test +- Better TypeScript integration +- Smaller bundle size + +### Cons โŒ +- Learning curve for class component developers +- Easy to create infinite loops in useEffect +- Performance pitfalls with dependencies +- Mental model shift required + +## Migration from Class Components + +### Class Component +```javascript +class UserProfile extends React.Component { + constructor(props) { + super(props); + this.state = { user: null, loading: true }; + } + + async componentDidMount() { + const user = await fetchUser(this.props.userId); + this.setState({ user, loading: false }); + } + + render() { + if (this.state.loading) return
Loading...
; + return
{this.state.user.name}
; + } +} +``` + +### Hooks Version +```javascript +function UserProfile({ userId }) { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetchUser(userId).then(user => { + setUser(user); + setLoading(false); + }); + }, [userId]); + + if (loading) return
Loading...
; + return
{user.name}
; +} +``` + +## Testing Hooks + +### Testing Custom Hooks +```javascript +import { renderHook, act } from '@testing-library/react-hooks'; +import { useCounter } from './useCounter'; + +describe('useCounter', () => { + it('should initialize with default value', () => { + const { result } = renderHook(() => useCounter(5)); + expect(result.current.count).toBe(5); + }); + + it('should increment count', () => { + const { result } = renderHook(() => useCounter()); + + act(() => { + result.current.increment(); + }); + + expect(result.current.count).toBe(1); + }); +}); +``` + +## Related Patterns + +- **Custom Hooks**: Extract and reuse stateful logic +- **Provider Pattern**: Often used with useContext +- **Compound Component**: Can be implemented with hooks +- **State Reducer**: useReducer for complex state management + +## Summary + +Hooks represent a paradigm shift in React development, enabling: +- Functional components with full React capabilities +- Better code reuse through custom hooks +- Simpler, more readable component code +- Improved testing and TypeScript experience + +The Hooks pattern has become the standard for modern React development, replacing class components in most use cases. \ No newline at end of file diff --git a/vanilla-patterns/README.md b/vanilla-patterns/README.md new file mode 100644 index 0000000..bde9ac7 --- /dev/null +++ b/vanilla-patterns/README.md @@ -0,0 +1,71 @@ +# Vanilla JavaScript Patterns + +This section contains fundamental patterns that work in any JavaScript environment, without framework dependencies. + +## ๐ŸŽฏ Design Patterns + +Classic software design patterns help solve common problems in software design. These patterns provide tested, proven development paradigms that can make your code more flexible, reusable, and maintainable. + +### Pattern Categories + +#### Creational Patterns +Deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. + +- **[Singleton](./design-patterns/singleton)** - Ensure a class has only one instance +- **[Factory](./design-patterns/factory)** - Create objects without specifying their exact classes +- **[Abstract Factory](./design-patterns/abstract-factory)** - Create families of related objects +- **[Builder](./design-patterns/builder)** - Construct complex objects step by step +- **[Prototype](./design-patterns/prototype)** - Clone objects using a prototypical instance + +#### Structural Patterns +Deal with object composition and typically identify simple ways to realize relationships between entities. + +- **[Adapter](./design-patterns/adapter)** - Allow incompatible interfaces to work together +- **[Bridge](./design-patterns/bridge)** - Separate abstraction from implementation +- **[Composite](./design-patterns/composite)** - Compose objects into tree structures +- **[Decorator](./design-patterns/decorator)** - Add behavior to objects dynamically +- **[Facade](./design-patterns/facade)** - Provide a simplified interface +- **[Flyweight](./design-patterns/flyweight)** - Share efficiently to support large numbers of objects +- **[Proxy](./design-patterns/proxy)** - Provide a placeholder/surrogate for another object + +#### Behavioral Patterns +Focus on communication between objects and the assignment of responsibilities. + +- **[Chain of Responsibility](./design-patterns/chain-of-responsibility)** - Pass requests along a chain +- **[Command](./design-patterns/command)** - Encapsulate requests as objects +- **[Iterator](./design-patterns/iterator)** - Access elements sequentially +- **[Mediator](./design-patterns/mediator)** - Define how objects interact +- **[Memento](./design-patterns/memento)** - Capture and restore object states +- **[Observer](./design-patterns/observer)** - Define a one-to-many dependency +- **[State](./design-patterns/state)** - Alter behavior when internal state changes +- **[Strategy](./design-patterns/strategy)** - Define a family of algorithms +- **[Template Method](./design-patterns/template-method)** - Define algorithm skeleton +- **[Visitor](./design-patterns/visitor)** - Define operations on object structures + +## โšก Performance Patterns + +Modern web applications need to be fast and efficient. These patterns help optimize loading, bundling, and runtime performance. + +- **[Bundling](./performance-patterns/bundling)** - Combine multiple files into single bundles +- **[Code Splitting](./performance-patterns/code-splitting)** - Split code into smaller chunks +- **[Tree Shaking](./performance-patterns/tree-shaking)** - Remove unused code +- **[Preload](./performance-patterns/preload)** - Load critical resources early +- **[Prefetch](./performance-patterns/prefetch)** - Load resources for next navigation +- **[Import on Visibility](./performance-patterns/import-on-visibility)** - Load when element becomes visible +- **[Import on Interaction](./performance-patterns/import-on-interaction)** - Load on user interaction +- **[Route-based Splitting](./performance-patterns/route-based-splitting)** - Split by application routes +- **[Bundle Splitting](./performance-patterns/bundle-splitting)** - Optimize bundle distribution + +## Getting Started + +1. Start with a simple pattern like **Singleton** to understand the basics +2. Move to **Observer** to learn about event-driven architecture +3. Explore **Factory** patterns for object creation +4. Learn performance patterns to optimize your applications + +Each pattern includes: +- Detailed explanation +- JavaScript implementation +- Practical examples +- Use cases and benefits +- Common pitfalls to avoid \ No newline at end of file diff --git a/vanilla-patterns/design-patterns/observer/README.md b/vanilla-patterns/design-patterns/observer/README.md new file mode 100644 index 0000000..5321991 --- /dev/null +++ b/vanilla-patterns/design-patterns/observer/README.md @@ -0,0 +1,303 @@ +# Observer Pattern + +The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. + +## Problem + +You need to maintain consistency between related objects without making them tightly coupled. For example: +- Model-View architectures +- Event handling systems +- Reactive programming +- State management + +## Solution + +The Observer pattern involves: +1. **Subject**: Maintains a list of observers and notifies them of changes +2. **Observer**: Defines an interface for objects that should be notified +3. **ConcreteObserver**: Implements the Observer interface + +## Implementation + +### Basic Observer Pattern + +```javascript +class Subject { + constructor() { + this.observers = []; + } + + addObserver(observer) { + this.observers.push(observer); + } + + removeObserver(observer) { + this.observers = this.observers.filter(obs => obs !== observer); + } + + notifyObservers(data) { + this.observers.forEach(observer => observer.update(data)); + } +} + +class Observer { + constructor(name) { + this.name = name; + } + + update(data) { + console.log(`${this.name} received:`, data); + } +} + +// Usage +const subject = new Subject(); +const observer1 = new Observer('Observer 1'); +const observer2 = new Observer('Observer 2'); + +subject.addObserver(observer1); +subject.addObserver(observer2); + +subject.notifyObservers('Hello Observers!'); +``` + +### Event-Driven Observer + +```javascript +class EventEmitter { + constructor() { + this.events = {}; + } + + on(event, callback) { + if (!this.events[event]) { + this.events[event] = []; + } + this.events[event].push(callback); + } + + off(event, callback) { + if (this.events[event]) { + this.events[event] = this.events[event].filter(cb => cb !== callback); + } + } + + emit(event, data) { + if (this.events[event]) { + this.events[event].forEach(callback => callback(data)); + } + } + + once(event, callback) { + const onceWrapper = (data) => { + callback(data); + this.off(event, onceWrapper); + }; + this.on(event, onceWrapper); + } +} +``` + +### Modern JavaScript Observer (using Proxy) + +```javascript +function createObservable(target, onChange) { + return new Proxy(target, { + set(obj, prop, value) { + const oldValue = obj[prop]; + obj[prop] = value; + onChange(prop, value, oldValue); + return true; + } + }); +} + +// Usage +const data = createObservable({ name: '', age: 0 }, (prop, newVal, oldVal) => { + console.log(`${prop} changed from ${oldVal} to ${newVal}`); +}); + +data.name = 'John'; // Triggers observer +data.age = 30; // Triggers observer +``` + +## Use Cases + +### โœ… Good Use Cases +- **UI Updates**: Model changes update multiple views +- **Event Systems**: DOM events, custom events +- **State Management**: Redux-like patterns +- **Real-time Updates**: WebSocket data updates +- **Logging**: Multiple loggers for different outputs +- **Caching**: Cache invalidation across systems + +### โŒ Avoid When +- Simple one-to-one relationships +- Performance is critical (many observers) +- Complex dependency chains +- Memory leaks are a concern + +## Pros and Cons + +### Pros โœ… +- Loose coupling between subject and observers +- Dynamic relationships (add/remove at runtime) +- Supports broadcast communication +- Follows Open/Closed Principle + +### Cons โŒ +- Can cause memory leaks if observers aren't removed +- Unexpected cascading updates +- Hard to debug complex notification chains +- Performance overhead with many observers + +## Modern JavaScript Alternatives + +### Custom Events +```javascript +// Using DOM events for decoupling +const eventBus = document.createElement('div'); + +// Observer +eventBus.addEventListener('userLogin', (event) => { + console.log('User logged in:', event.detail); +}); + +// Subject +function loginUser(userData) { + // Login logic... + eventBus.dispatchEvent(new CustomEvent('userLogin', { + detail: userData + })); +} +``` + +### RxJS Observables +```javascript +import { Subject } from 'rxjs'; + +const subject = new Subject(); + +// Observer +subject.subscribe(data => console.log('Observer 1:', data)); +subject.subscribe(data => console.log('Observer 2:', data)); + +// Emit data +subject.next('Hello RxJS!'); +``` + +### Vue.js Reactivity +```javascript +import { ref, watchEffect } from 'vue'; + +const count = ref(0); + +// Observer +watchEffect(() => { + console.log('Count changed:', count.value); +}); + +// Subject +count.value = 1; // Triggers observer +``` + +## Implementation Patterns + +### Push vs Pull + +**Push Model** (Subject pushes data to observers): +```javascript +notifyObservers(data) { + this.observers.forEach(observer => observer.update(data)); +} +``` + +**Pull Model** (Observers pull data from subject): +```javascript +notifyObservers() { + this.observers.forEach(observer => observer.update(this)); +} +``` + +### Filtered Notifications +```javascript +class FilteredSubject { + constructor() { + this.observers = new Map(); + } + + addObserver(observer, filter = () => true) { + this.observers.set(observer, filter); + } + + notifyObservers(data) { + this.observers.forEach((filter, observer) => { + if (filter(data)) { + observer.update(data); + } + }); + } +} +``` + +## Testing + +Observer patterns are generally easy to test: + +```javascript +describe('Observer Pattern', () => { + let subject, observer1, observer2; + + beforeEach(() => { + subject = new Subject(); + observer1 = new Observer('Test Observer 1'); + observer2 = new Observer('Test Observer 2'); + }); + + test('should notify all observers', () => { + const spy1 = jest.spyOn(observer1, 'update'); + const spy2 = jest.spyOn(observer2, 'update'); + + subject.addObserver(observer1); + subject.addObserver(observer2); + + subject.notifyObservers('test data'); + + expect(spy1).toHaveBeenCalledWith('test data'); + expect(spy2).toHaveBeenCalledWith('test data'); + }); +}); +``` + +## Related Patterns + +- **Model-View-Controller**: Uses Observer to keep views synchronized +- **Mediator**: Can use Observer for communication between components +- **Command**: Can use Observer to notify about command execution +- **State**: Can use Observer to notify about state changes + +## Memory Management + +Always remember to clean up observers to prevent memory leaks: + +```javascript +class Component { + constructor() { + this.cleanup = []; + } + + subscribe(subject, callback) { + subject.addObserver(callback); + this.cleanup.push(() => subject.removeObserver(callback)); + } + + destroy() { + this.cleanup.forEach(fn => fn()); + this.cleanup = []; + } +} +``` + +## Summary + +The Observer pattern is fundamental for creating loosely coupled, event-driven systems. While modern JavaScript offers many alternatives (Promises, async/await, reactive frameworks), understanding the Observer pattern helps you recognize and implement these concepts effectively in your applications. \ No newline at end of file diff --git a/vanilla-patterns/design-patterns/observer/index.js b/vanilla-patterns/design-patterns/observer/index.js new file mode 100644 index 0000000..5a17b7f --- /dev/null +++ b/vanilla-patterns/design-patterns/observer/index.js @@ -0,0 +1,237 @@ +/** + * Observer Pattern Implementation + * + * Defines a one-to-many dependency between objects so that when one object + * changes state, all its dependents are notified automatically. + */ + +// Basic Observer Pattern +class Subject { + constructor() { + this.observers = []; + this.state = null; + } + + // Add observer to the list + addObserver(observer) { + if (!this.observers.includes(observer)) { + this.observers.push(observer); + } + } + + // Remove observer from the list + removeObserver(observer) { + this.observers = this.observers.filter(obs => obs !== observer); + } + + // Notify all observers of state change + notifyObservers(data = null) { + this.observers.forEach(observer => { + observer.update(data || this.state, this); + }); + } + + // Set state and notify observers + setState(state) { + this.state = state; + this.notifyObservers(); + } + + getState() { + return this.state; + } + + getObserverCount() { + return this.observers.length; + } +} + +// Observer interface +class Observer { + constructor(name) { + this.name = name; + } + + update(data, subject) { + console.log(`${this.name} received update:`, data); + } +} + +// Event Emitter Pattern (Modern Observer) +class EventEmitter { + constructor() { + this.events = new Map(); + } + + // Subscribe to an event + on(event, callback) { + if (!this.events.has(event)) { + this.events.set(event, []); + } + this.events.get(event).push(callback); + + // Return unsubscribe function + return () => this.off(event, callback); + } + + // Unsubscribe from an event + off(event, callback) { + if (this.events.has(event)) { + const callbacks = this.events.get(event); + const index = callbacks.indexOf(callback); + if (index > -1) { + callbacks.splice(index, 1); + } + + // Clean up empty event arrays + if (callbacks.length === 0) { + this.events.delete(event); + } + } + } + + // Subscribe to an event only once + once(event, callback) { + const onceWrapper = (...args) => { + callback(...args); + this.off(event, onceWrapper); + }; + return this.on(event, onceWrapper); + } + + // Emit an event to all subscribers + emit(event, ...args) { + if (this.events.has(event)) { + // Create a copy to avoid issues if callbacks modify the array + const callbacks = [...this.events.get(event)]; + callbacks.forEach(callback => { + try { + callback(...args); + } catch (error) { + console.error(`Error in event handler for "${event}":`, error); + } + }); + } + } + + // Get list of events + eventNames() { + return Array.from(this.events.keys()); + } + + // Get number of listeners for an event + listenerCount(event) { + return this.events.has(event) ? this.events.get(event).length : 0; + } + + // Remove all listeners + removeAllListeners(event = null) { + if (event) { + this.events.delete(event); + } else { + this.events.clear(); + } + } +} + +// Observable with Proxy (Modern JavaScript) +function createObservable(target, handler = {}) { + const { + get: getHandler = () => {}, + set: setHandler = () => {}, + deleteProperty: deleteHandler = () => {} + } = handler; + + return new Proxy(target, { + get(obj, prop) { + getHandler(prop, obj[prop]); + return obj[prop]; + }, + + set(obj, prop, value) { + const oldValue = obj[prop]; + obj[prop] = value; + setHandler(prop, value, oldValue); + return true; + }, + + deleteProperty(obj, prop) { + const oldValue = obj[prop]; + delete obj[prop]; + deleteHandler(prop, oldValue); + return true; + } + }); +} + +// Filtered Observer (only notifies when condition is met) +class FilteredSubject extends Subject { + constructor() { + super(); + this.observerFilters = new Map(); + } + + addObserver(observer, filter = () => true) { + super.addObserver(observer); + this.observerFilters.set(observer, filter); + } + + removeObserver(observer) { + super.removeObserver(observer); + this.observerFilters.delete(observer); + } + + notifyObservers(data = null) { + this.observers.forEach(observer => { + const filter = this.observerFilters.get(observer); + if (filter && filter(data || this.state)) { + observer.update(data || this.state, this); + } + }); + } +} + +// Async Observer (for handling async operations) +class AsyncSubject extends Subject { + async notifyObserversAsync(data = null) { + const promises = this.observers.map(async observer => { + try { + await observer.updateAsync(data || this.state, this); + } catch (error) { + console.error('Error in async observer:', error); + } + }); + + await Promise.all(promises); + } + + async setStateAsync(state) { + this.state = state; + await this.notifyObserversAsync(); + } +} + +class AsyncObserver extends Observer { + async updateAsync(data, subject) { + // Simulate async operation + await new Promise(resolve => setTimeout(resolve, 100)); + console.log(`${this.name} async update:`, data); + } + + // Fallback for sync calls + update(data, subject) { + this.updateAsync(data, subject); + } +} + +export { + Subject, + Observer, + EventEmitter, + createObservable, + FilteredSubject, + AsyncSubject, + AsyncObserver +}; + +export default Subject; \ No newline at end of file diff --git a/vanilla-patterns/design-patterns/singleton/README.md b/vanilla-patterns/design-patterns/singleton/README.md new file mode 100644 index 0000000..efa2eda --- /dev/null +++ b/vanilla-patterns/design-patterns/singleton/README.md @@ -0,0 +1,208 @@ +# Singleton Pattern + +The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. + +## Problem + +Sometimes you need exactly one instance of a class. For example: +- Database connection pools +- Logger instances +- Configuration objects +- Cache managers + +## Solution + +The Singleton pattern solves this by: +1. Making the constructor private +2. Providing a static method that returns the single instance +3. Storing the instance in a static variable + +## Implementation + +### Basic JavaScript Singleton + +```javascript +class Singleton { + constructor() { + if (Singleton.instance) { + return Singleton.instance; + } + + Singleton.instance = this; + this.data = []; + return this; + } + + addData(item) { + this.data.push(item); + } + + getData() { + return this.data; + } +} + +// Usage +const singleton1 = new Singleton(); +const singleton2 = new Singleton(); + +console.log(singleton1 === singleton2); // true +``` + +### ES6 Module Singleton + +```javascript +let instance = null; + +class DatabaseConnection { + constructor() { + if (instance) { + return instance; + } + + this.connection = this.connect(); + instance = this; + return this; + } + + connect() { + console.log('Creating database connection...'); + return { connected: true, id: Date.now() }; + } + + query(sql) { + console.log(`Executing query: ${sql}`); + return { results: [] }; + } +} + +export default DatabaseConnection; +``` + +### Functional Singleton + +```javascript +const createSingleton = (function() { + let instance; + + function createInstance() { + return { + data: [], + addItem(item) { + this.data.push(item); + }, + getItems() { + return this.data; + } + }; + } + + return function() { + if (!instance) { + instance = createInstance(); + } + return instance; + }; +})(); + +// Usage +const singleton1 = createSingleton(); +const singleton2 = createSingleton(); + +console.log(singleton1 === singleton2); // true +``` + +## Use Cases + +### โœ… Good Use Cases +- **Configuration objects**: Application settings that should be consistent +- **Logging**: Central logging system +- **Database connections**: Connection pools and managers +- **Caches**: Application-wide caching systems +- **State management**: Global application state + +### โŒ Avoid When +- You need multiple instances +- Testing becomes difficult (hard to mock) +- It creates hidden dependencies +- It violates single responsibility principle + +## Pros and Cons + +### Pros โœ… +- Guaranteed single instance +- Global access point +- Lazy initialization +- Memory efficient + +### Cons โŒ +- Difficult to test (global state) +- Hidden dependencies +- Violates Single Responsibility Principle +- Can become a "god object" +- Makes code tightly coupled + +## Modern Alternatives + +In modern JavaScript, consider these alternatives: + +### ES6 Modules +```javascript +// config.js +export const config = { + apiUrl: 'https://api.example.com', + timeout: 5000 +}; + +// Modules are singletons by nature +``` + +### Dependency Injection +```javascript +class UserService { + constructor(database, logger) { + this.database = database; + this.logger = logger; + } +} + +// Inject dependencies instead of using global singletons +``` + +## Testing + +Singletons can be difficult to test. Here are some strategies: + +```javascript +class TestableConfig { + constructor() { + if (TestableConfig.instance && !process.env.NODE_ENV === 'test') { + return TestableConfig.instance; + } + + this.settings = {}; + TestableConfig.instance = this; + return this; + } + + // Add reset method for testing + static reset() { + TestableConfig.instance = null; + } +} + +// In tests +afterEach(() => { + TestableConfig.reset(); +}); +``` + +## Related Patterns + +- **Factory Pattern**: Often used together to control object creation +- **Abstract Factory**: May use Singleton to ensure single factory instance +- **Builder Pattern**: The director can be a Singleton + +## Summary + +The Singleton pattern ensures single instance creation but comes with trade-offs in testability and coupling. In modern JavaScript development, consider ES6 modules or dependency injection as alternatives that provide similar benefits with fewer drawbacks. \ No newline at end of file diff --git a/vanilla-patterns/design-patterns/singleton/example/demo.js b/vanilla-patterns/design-patterns/singleton/example/demo.js new file mode 100644 index 0000000..a4ea710 --- /dev/null +++ b/vanilla-patterns/design-patterns/singleton/example/demo.js @@ -0,0 +1,191 @@ +/** + * Singleton Pattern Examples + * + * Practical examples of using the Singleton pattern + */ + +import { Singleton, FunctionalSingleton, ModuleSingleton } from '../index.js'; + +// Example 1: Basic Singleton Usage +console.log('=== Example 1: Basic Singleton ==='); + +const singleton1 = new Singleton(); +const singleton2 = new Singleton(); + +console.log('Are instances equal?', singleton1 === singleton2); // true + +singleton1.addData('First item'); +singleton2.addData('Second item'); + +console.log('Data from singleton1:', singleton1.getData()); +console.log('Data from singleton2:', singleton2.getData()); + +// Example 2: Using Static Method +console.log('\n=== Example 2: Static Method ==='); + +const singleton3 = Singleton.getInstance(); +const singleton4 = Singleton.getInstance(); + +console.log('Static instances equal?', singleton3 === singleton4); // true +console.log('Same as previous instances?', singleton3 === singleton1); // true + +// Example 3: Functional Singleton +console.log('\n=== Example 3: Functional Singleton ==='); + +const funcSingleton1 = FunctionalSingleton.getInstance(); +const funcSingleton2 = FunctionalSingleton.getInstance(); + +console.log('Functional instances equal?', funcSingleton1 === funcSingleton2); // true + +funcSingleton1.addItem('Item 1'); +funcSingleton2.addItem('Item 2'); + +console.log('Items:', funcSingleton1.getItems()); +console.log('Item count:', funcSingleton2.getItemCount()); + +// Example 4: Configuration Singleton +console.log('\n=== Example 4: Configuration Singleton ==='); + +const config1 = new ModuleSingleton(); +const config2 = new ModuleSingleton(); + +console.log('Config instances equal?', config1 === config2); // true + +config1.init({ + apiUrl: 'https://api.example.com', + timeout: 5000, + retries: 3 +}); + +console.log('Config from instance 1:', config2.getConfig()); +config2.setConfig('timeout', 10000); +console.log('Updated timeout:', config1.getConfig('timeout')); + +// Example 5: Database Connection Simulator +console.log('\n=== Example 5: Database Connection ==='); + +class DatabaseConnection { + constructor() { + if (DatabaseConnection.instance) { + return DatabaseConnection.instance; + } + + this.connectionId = Math.random().toString(36).substr(2, 9); + this.connected = false; + this.queries = []; + + DatabaseConnection.instance = this; + return this; + } + + connect() { + if (!this.connected) { + console.log(`Connecting to database with ID: ${this.connectionId}`); + this.connected = true; + } else { + console.log('Already connected to database'); + } + return this; + } + + disconnect() { + if (this.connected) { + console.log('Disconnecting from database'); + this.connected = false; + } + return this; + } + + query(sql) { + if (!this.connected) { + throw new Error('Not connected to database'); + } + + this.queries.push({ sql, timestamp: new Date() }); + console.log(`Executing: ${sql}`); + return { success: true, results: [] }; + } + + getConnectionInfo() { + return { + id: this.connectionId, + connected: this.connected, + totalQueries: this.queries.length + }; + } +} + +const db1 = new DatabaseConnection(); +const db2 = new DatabaseConnection(); + +console.log('Database instances equal?', db1 === db2); // true + +db1.connect(); +db2.query('SELECT * FROM users'); +db1.query('SELECT * FROM products'); + +console.log('Connection info:', db2.getConnectionInfo()); + +// Example 6: Logger Singleton +console.log('\n=== Example 6: Logger Singleton ==='); + +class Logger { + constructor() { + if (Logger.instance) { + return Logger.instance; + } + + this.logs = []; + this.level = 'INFO'; + Logger.instance = this; + return this; + } + + setLevel(level) { + this.level = level; + return this; + } + + log(message, level = 'INFO') { + const logEntry = { + message, + level, + timestamp: new Date().toISOString(), + id: this.logs.length + 1 + }; + + this.logs.push(logEntry); + console.log(`[${logEntry.timestamp}] ${level}: ${message}`); + return this; + } + + info(message) { + return this.log(message, 'INFO'); + } + + warn(message) { + return this.log(message, 'WARN'); + } + + error(message) { + return this.log(message, 'ERROR'); + } + + getLogs(level = null) { + return level ? + this.logs.filter(log => log.level === level) : + this.logs; + } +} + +const logger1 = new Logger(); +const logger2 = new Logger(); + +console.log('Logger instances equal?', logger1 === logger2); // true + +logger1.info('Application started'); +logger2.warn('This is a warning'); +logger1.error('Something went wrong'); + +console.log(`Total logs: ${logger2.getLogs().length}`); +console.log(`Error logs: ${logger1.getLogs('ERROR').length}`); \ No newline at end of file diff --git a/vanilla-patterns/design-patterns/singleton/index.js b/vanilla-patterns/design-patterns/singleton/index.js new file mode 100644 index 0000000..df63c6c --- /dev/null +++ b/vanilla-patterns/design-patterns/singleton/index.js @@ -0,0 +1,122 @@ +/** + * Singleton Pattern Implementation + * + * Ensures a class has only one instance and provides global access to it. + */ + +// Class-based Singleton +class Singleton { + constructor() { + // Check if instance already exists + if (Singleton.instance) { + return Singleton.instance; + } + + // Initialize the instance + this.data = []; + this.createdAt = new Date(); + + // Store the instance + Singleton.instance = this; + return this; + } + + addData(item) { + this.data.push(item); + } + + getData() { + return this.data; + } + + getCreationTime() { + return this.createdAt; + } + + // Static method to get instance + static getInstance() { + if (!Singleton.instance) { + Singleton.instance = new Singleton(); + } + return Singleton.instance; + } +} + +// Functional Singleton using IIFE (Immediately Invoked Function Expression) +const FunctionalSingleton = (function() { + let instance; + + function createInstance() { + // Private variables and methods + let privateData = []; + + return { + // Public methods + addItem(item) { + privateData.push(item); + }, + + getItems() { + return [...privateData]; // Return copy to prevent direct mutation + }, + + getItemCount() { + return privateData.length; + }, + + clearItems() { + privateData = []; + } + }; + } + + return { + getInstance() { + if (!instance) { + instance = createInstance(); + } + return instance; + } + }; +})(); + +// Module-based Singleton (ES6 modules are singletons by nature) +let moduleInstance = null; + +class ModuleSingleton { + constructor() { + if (moduleInstance) { + return moduleInstance; + } + + this.config = {}; + this.initialized = false; + moduleInstance = this; + return this; + } + + init(config) { + if (!this.initialized) { + this.config = { ...config }; + this.initialized = true; + } + return this; + } + + getConfig(key) { + return key ? this.config[key] : this.config; + } + + setConfig(key, value) { + if (this.initialized) { + this.config[key] = value; + } + return this; + } +} + +// Export the class (the module system ensures singleton behavior) +export { Singleton, FunctionalSingleton, ModuleSingleton }; + +// Default export for main singleton +export default Singleton; \ No newline at end of file