面向對象與面向過程、函數式編程
1. 面向過程編程(Procedure-Oriented Programming)
面向過程編程將程序視為一系列函數的集合,數據和操作數據的函數是分離的。在 Vue
3 中,這種風格通常表現為使用組合式 API
(Composition API
)以函數式的方式組織代碼。
示例:Todo 應用(面向過程)
// Todo 應用 - 面向過程實現
import { ref, computed, onMounted } from 'vue';// 定義數據結構
interface Todo {id: number;title: string;completed: boolean;
}// 狀態管理(使用 ref 和 reactive)
const todos = ref<Todo[]>([]);
const newTodoTitle = ref('');// 計算屬性
const completedTodos = computed(() => todos.value.filter(todo => todo.completed)
);// 方法
const addTodo = () => {if (newTodoTitle.value.trim()) {todos.value.push({id: Date.now(),title: newTodoTitle.value,completed: false});newTodoTitle.value = '';}
};const toggleTodo = (id: number) => {const todo = todos.value.find(t => t.id === id);if (todo) {todo.completed = !todo.completed;}
};const deleteTodo = (id: number) => {todos.value = todos.value.filter(t => t.id !== id);
};// 生命周期鉤子
onMounted(() => {// 從本地存儲加載數據const savedTodos = localStorage.getItem('todos');if (savedTodos) {todos.value = JSON.parse(savedTodos);}
});// 監聽變化,保存到本地存儲
watch(todos, (newTodos) => {localStorage.setItem('todos', JSON.stringify(newTodos));
});// 導出所有需要在模板中使用的內容
export {todos,newTodoTitle,completedTodos,addTodo,toggleTodo,deleteTodo
};
特點
- 數據和邏輯分離:狀態(
todos
、newTodoTitle
)和操作(addTodo
、toggleTodo
)是分開定義的。 - 線性結構:代碼按照功能邏輯組織成獨立的函數。
- 靈活組合:可以輕松地復用單個函數或組合多個功能。
2. 面向對象編程(Object-Oriented Programming)
面向對象編程將數據和操作數據的方法封裝在一起,形成對象,通過類來創建對象實例。在 Vue 3
中,可以使用類組件或在組合式 API
中封裝類來實現。
示例:Todo 應用(面向對象)
// Todo 應用 - 面向對象實現
import { ref, computed, onMounted, watch } from 'vue';// 定義 Todo 類
class Todo {constructor(public id: number,public title: string,public completed: boolean = false) {}toggle() {this.completed = !this.completed;}
}// Todo 管理器類
class TodoManager {private _todos = ref<Todo[]>([]);private _newTodoTitle = ref('');constructor() {// 初始化onMounted(() => this.loadFromLocalStorage());watch(this._todos, () => this.saveToLocalStorage());}// 獲取 todos 的計算屬性get todos() {return this._todos;}get newTodoTitle() {return this._newTodoTitle;}get completedTodos() {return computed(() => this._todos.value.filter(todo => todo.completed));}// 方法addTodo() {if (this._newTodoTitle.value.trim()) {this._todos.value.push(new Todo(Date.now(), this._newTodoTitle.value));this._newTodoTitle.value = '';}}toggleTodo(id: number) {const todo = this._todos.value.find(t => t.id === id);todo?.toggle();}deleteTodo(id: number) {this._todos.value = this._todos.value.filter(t => t.id !== id);}// 私有方法private saveToLocalStorage() {localStorage.setItem('todos',JSON.stringify(this._todos.value));}private loadFromLocalStorage() {const savedTodos = localStorage.getItem('todos');if (savedTodos) {this._todos.value = JSON.parse(savedTodos).map((todo: any) => new Todo(todo.id, todo.title, todo.completed));}}
}// 創建單例實例
const todoManager = new TodoManager();// 導出需要在模板中使用的內容
export {todoManager
};
特點
- 封裝性:將數據(
_todos
、_newTodoTitle
)和操作(addTodo
、toggleTodo
)封裝在TodoManager
類中。 - 繼承與多態:可以通過繼承擴展功能(例如創建
AdvancedTodoManager
)。 - 狀態內聚:相關的狀態和方法被組織在一起,便于維護和復用。
維度 | 面向過程 | 面向對象 |
---|---|---|
代碼結構 | 函數和數據分離,線性組織 | 數據和方法封裝在類中,層次化組織 |
復用方式 | 函數復用 | 類的繼承和組合 |
狀態管理 | 分散管理(多個 ref/reactive) | 集中管理(類的實例屬性) |
適用場景 | 簡單邏輯、工具函數、快速迭代 | 復雜業務邏輯、需要高內聚低耦合的系統 |
Vue 3 實現方式 | 組合式 API | 類組件或在組合式 API 中封裝類 |
在實際項目中,通常會混合使用兩種范式。例如:
- 使用面向過程的方式處理簡單邏輯
- 使用面向對象的方式封裝復雜業務領域模型
// 混合使用示例:在組合式 API 中使用類
import { ref, computed, onMounted } from 'vue';// 定義領域模型(面向對象)
class User {constructor(public id: number, public name: string) {}changeName(newName: string) {this.name = newName;}
}// 在組合式 API 中使用(面向過程風格)
export function useUser() {const currentUser = ref<User | null>(null);const fetchUser = async (id: number) => {// API 請求...const userData = { id, name: 'John' };currentUser.value = new User(userData.id, userData.name);};const userName = computed(() => currentUser.value?.name || 'Guest');return {currentUser,userName,fetchUser};
}
- 面向過程適合簡單、靈活的場景,在 Vue 3 中自然地體現為組合式 API 的使用。
- 面向對象適合復雜、需要高內聚的場景,可以通過類來封裝業務邏輯。
3.函數式編程
3.1 對比
編程范式 | 核心思想 | 關鍵特性 |
---|---|---|
面向過程(POP) | 將程序分解為一系列步驟(函數),數據與操作分離 | 函數、順序執行、共享狀態 |
面向對象(OOP) | 將數據和操作封裝為對象,通過類創建實例,強調繼承和多態 | 類、對象、繼承、封裝、多態 |
函數式編程(FP) | 將計算視為函數的求值,避免共享狀態和可變數據,強調純函數和不可變性 | 純函數、不可變數據、高階函數、函數組合、無副作用 |
3.2 函數式編程與面向過程的關聯
3.2.1 相似點
- 函數為基本單元:兩者都使用函數作為代碼的基本構建塊。
- 過程式執行:都可以按照步驟順序執行代碼。
3.2.2 不同點
- 狀態管理:
- POP:依賴共享狀態和可變數據。
- FP:避免共享狀態,強調不可變性和純函數。
- 函數特性:
- POP:函數可以有副作用,修改外部狀態。
- FP:函數是純的,不產生副作用,相同輸入始終返回相同輸出。
POP 風格:
// 面向過程:使用共享狀態和有副作用的函數
const count = ref(0);const increment = () => {count.value++; // 修改共享狀態
};
FP 風格:
// 函數式:使用不可變數據和純函數
const state = ref({ count: 0 });const increment = () => {state.value = { ...state.value, count: state.value.count + 1 }; // 創建新對象
};
3.3 函數式編程與面向對象的關聯
3.3.1 互補性
- OOP擅長處理復雜的狀態管理和對象關系(如繼承、多態)。
- FP擅長處理數據轉換和無副作用的操作。
3.3.2 不同點
- 封裝方式:
- OOP:通過類和對象封裝數據和行為。
- FP:通過函數封裝行為,數據和操作分離。
- 數據流動:
- OOP:通過對象方法修改內部狀態(可變數據)。
- FP:通過純函數產生新數據(不可變數據)。
OOP 管理領域模型:
class ShoppingCart {private items = ref<Product[]>([]);addItem(product: Product) {this.items.value = [...this.items.value, product]; // 使用 FP 思想的不可變更新}
}
FP 處理數據轉換:
// 使用純函數處理數據
const calculateTotal = (items: Product[]) => items.reduce((total, item) => total + item.price, 0);
3.4. 函數式編程在 Vue 3 中的典型應用
3.4.1 純函數組件
// 純函數組件(無狀態)
const Button = (props: { label: string; onClick: () => void }) => {return (<button onClick={props.onClick}>{props.label}</button>);
};
3.4.2 不可變數據處理
// 使用 Immer 簡化不可變數據操作
import produce from 'immer';const updateUser = (userId: number, newData: Partial<User>) => {users.value = produce(users.value, draft => {const user = draft.find(u => u.id === userId);if (user) {Object.assign(user, newData);}});
};
3.4.3 高階函數與組合
// 高階函數:創建可復用的狀態處理器
const withLoading = <T>(fetcher: () => Promise<T>) => {const loading = ref(false);const data = ref<T | null>(null);const error = ref<string | null>(null);const execute = async () => {loading.value = true;try {data.value = await fetcher();} catch (err) {error.value = err.message;} finally {loading.value = false;}};return { loading, data, error, execute };
};
3.5. 三種范式的協作模式
3.5.1 分層架構
- 表現層:
Vue
組件(可以是OOP
或POP
風格) - 業務邏輯層:領域模型(
OOP
)+ 純函數(FP
) - 數據訪問層:函數式的數據轉換(
FP
)
3.5.2 混合使用示例
// 組合式 API 中混合使用三種范式
import { ref, computed } from 'vue';// OOP:領域模型
class Todo {constructor(public id: number, public title: string) {}markAsCompleted() {return new Todo(this.id, this.title, true); // 使用 FP 思想創建新對象}
}// FP:純函數處理數據
const filterTodos = (todos: Todo[], status: 'all' | 'completed' | 'active') => {if (status === 'completed') return todos.filter(t => t.completed);if (status === 'active') return todos.filter(t => !t.completed);return todos;
};// POP:組合式 API 組織邏輯
export function useTodos() {const todos = ref<Todo[]>([]);const filter = ref<'all' | 'completed' | 'active'>('all');const filteredTodos = computed(() => filterTodos(todos.value, filter.value));const addTodo = (title: string) => {todos.value = [...todos.value, new Todo(Date.now(), title)];};return {todos,filter,filteredTodos,addTodo};
}
場景 | 推薦范式 | 理由 |
---|---|---|
簡單腳本或工具函數 | 面向過程(POP) | 快速實現,邏輯直接 |
復雜業務對象建模 | 面向對象(OOP) | 封裝狀態和行為,利用繼承和多態 |
數據處理、流式計算 | 函數式編程(FP) | 純函數和不可變性避免副作用,便于測試和調試 |
狀態管理與組件交互 | 混合使用 | Vue 3 的組合式 API 天然支持多種范式,根據具體需求選擇或組合使用 |