Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Как адаптер связан с Cohesion?
Это отличный вопрос о взаимосвязи паттерна Adapter (Адаптер) и Cohesion (Связанность). Хотя это разные концепции, они тесно связаны через принцип правильной архитектуры.
Что такое Adapter паттерн
Adapter - это структурный паттерн проектирования, который позволяет использовать несовместимые интерфейсы. Адаптер преобразует интерфейс одного класса/компонента в интерфейс, который ожидает другой класс.
Примеры:
// Старый API, который нельзя менять
class OldAPIResponse {
getUserData() {
return {
name: 'John',
age: 30,
email_address: 'john@example.com'
};
}
}
// Новый API ожидает другой формат
interface User {
fullName: string;
age: number;
email: string;
}
// Адаптер - преобразует старый формат в новый
function adaptOldToNew(oldData: ReturnType<OldAPIResponse['getUserData']>): User {
return {
fullName: oldData.name,
age: oldData.age,
email: oldData.email_address
};
}
const oldAPI = new OldAPIResponse();
const user: User = adaptOldToNew(oldAPI.getUserData());
Что такое Cohesion
Cohesion (связанность) - это мера того, насколько сильно элементы компонента связаны между собой. Высокая связанность (high cohesion) означает, что элементы компонента имеют одну общую цель.
Низкая связанность (bad):
// Компонент отвечает за слишком много
function UserManager() {
// Управление пользователями
const [users, setUsers] = useState([]);
// Форматирование дат (не связано с пользователями)
const formatDate = (date) => new Date(date).toLocaleDateString();
// Работа с платежами (совсем не связано)
const processPayment = async (amount) => {
// ...
};
// Обработка аналитики (не связано)
const trackEvent = (event) => {
// ...
};
return <div>User Manager</div>;
}
Высокая связанность (good):
// Компонент отвечает за одно - управление пользователями
function UserManager() {
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
const response = await fetch('/api/users');
setUsers(await response.json());
};
const deleteUser = async (id) => {
await fetch(`/api/users/${id}`, { method: 'DELETE' });
setUsers(users.filter(u => u.id !== id));
};
useEffect(() => { fetchUsers(); }, []);
return (
<div>
{users.map(user => (
<UserCard key={user.id} user={user} onDelete={deleteUser} />
))}
</div>
);
}
Как Adapter улучшает Cohesion
Адаптер позволяет сохранять высокую связанность, изолируя несовместимые интерфейсы:
// API A возвращает данные в одном формате
const apiA = {
getUser: async (id) => ({
userId: id,
userName: 'Alice',
userAge: 25
})
};
// API B возвращает в другом формате
const apiB = {
fetchUser: async (id) => ({
id,
name: 'Bob',
age: 30
})
};
// Наш компонент ожидает единый интерфейс
interface User {
id: string;
name: string;
age: number;
}
// Адаптеры для обеих API
const userAdapters = {
adaptApiA: (data): User => ({
id: data.userId,
name: data.userName,
age: data.userAge
}),
adaptApiB: (data): User => ({
id: data.id,
name: data.name,
age: data.age
})
};
// Компонент имеет одну ответственность - отображение пользователя
function UserProfile({ userId, apiVersion = 'A' }) {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
const fetchUser = apiVersion === 'A' ? apiA.getUser : apiB.fetchUser;
const adapter = apiVersion === 'A'
? userAdapters.adaptApiA
: userAdapters.adaptApiB;
fetchUser(userId).then(data => setUser(adapter(data)));
}, [userId, apiVersion]);
if (!user) return <div>Loading...</div>;
return (
<div>
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
</div>
);
}
Adapter для Redux/состояния
Адаптеры часто используются для нормализации данных перед сохранением в Redux:
// API данные
interface APIProduct {
product_id: number;
product_name: string;
product_price: number;
in_stock: boolean;
}
// Redux состояние
interface Product {
id: number;
name: string;
price: number;
inStock: boolean;
}
// Адаптер
function adaptProductFromAPI(apiProduct: APIProduct): Product {
return {
id: apiProduct.product_id,
name: apiProduct.product_name,
price: apiProduct.product_price,
inStock: apiProduct.in_stock
};
}
// Reducer имеет одну ответственность - управлять состоянием
function productReducer(state = [], action) {
switch (action.type) {
case 'LOAD_PRODUCTS':
// Преобразуем API данные в Redux формат через адаптер
return action.payload.map(adaptProductFromAPI);
default:
return state;
}
}
Adapter в интеграции третьих библиотек
// Третья библиотека DatePicker имеет свой формат
type LibraryDate = Date;
// Наше приложение использует другой формат
type AppDate = string; // 'YYYY-MM-DD'
// Адаптер для DatePicker
function DatePickerAdapter(props) {
const [value, setValue] = useState<AppDate>('');
const handleChange = (libraryDate: LibraryDate) => {
// Преобразуем из формата библиотеки в формат приложения
const appDate = libraryDate.toISOString().split('T')[0];
setValue(appDate);
props.onChange?.(appDate);
};
return (
<DatePicker
value={value ? new Date(value) : undefined}
onChange={handleChange}
/>
);
}
// Остальное приложение работает с единым форматом дат
function BookingForm() {
const [date, setDate] = useState<AppDate>('');
return (
<DatePickerAdapter value={date} onChange={setDate} />
);
}
Взаимосвязь Adapter и Cohesion
-
Адаптер скрывает сложность - компонент остаётся сосредоточен на одной задаче
-
Адаптер разделяет ответственность - трансформация данных отделена от логики
-
Адаптер облегчает тестирование - можно тестировать адаптер отдельно от компонента
// Тест для адаптера (одна ответственность)
describe('User Adapter', () => {
it('should adapt API response to app format', () => {
const apiResponse = {
userId: '123',
userName: 'John',
userAge: 30
};
const result = userAdapters.adaptApiA(apiResponse);
expect(result).toEqual({
id: '123',
name: 'John',
age: 30
});
});
});
// Тест для компонента (не заботится о деталях адаптации)
describe('UserProfile', () => {
it('should display user data', () => {
const user = { id: '1', name: 'John', age: 30 };
// Тестируем компонент с уже адаптированными данными
render(<UserProfile user={user} />);
expect(screen.getByText('John')).toBeInTheDocument();
});
});
Когда НЕ использовать Adapter
- Если интерфейсы совместимы - адаптер добавляет ненужную сложность
- Если адаптер используется только в одном месте - проще встроить трансформацию
- Если данные меняются часто - мёртвый код
Вывод: Adapter паттерн и Cohesion связаны тем, что правильно использованный адаптер повышает связанность компонентов. Адаптер преобразует несовместимые интерфейсы, позволяя каждому компоненту сохранять одну чёткую ответственность. Это следствие принципа "Адаптер разделяет ответственность между компонентами".