8장 기능 이동 #11
Replies: 6 comments
-
본문의 코드를 리팩터링 해봤습니다. // 주문 처리 시스템 예제
class Order {
constructor(customer) {
this.customer = customer;
this.items = [];
}
addItem(item, quantity) {
this.items.push({
item: item,
quantity: quantity,
});
}
validateStock() {
const result = this.items.find(({ item, quantity }) => item.stock < quantity);
if (result) {
throw new Error('재고가 부족합니다.');
}
}
calculateTotalBasePrice() {
return this.items.reduce((total, item) => total + item.calculateBasePrice(item.quantity), 0);
}
calculateDiscountedPirce(totalBasePrice) {
return totalBasePrice * (1 - this.customer.discountRate);
}
calculateShippingCost(total) {
return total > 50000 ? 0 : 5000;
}
calculateTotal() {
this.validateStock();
const totalBasePrice = this.calculateTotalBasePrice();
return this.calculateDiscountedPirce(totalBasePrice) + this.calculateShippingCost(totalBasePrice);
}
}
class Customer {
constructor(name, isPremium) {
this.name = name;
this.isPremium = isPremium;
this._setDiscountRate();
}
get discountRate() {
return this.discountRate;
}
_setDiscountRate() {
if (this.isPremium) {
this.discountRate = 0.1;
} else {
this.discountRate = 0.05;
}
}
}
class OrderItem {
constructor(name, price, stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
calculateBasePrice(quantity) {
return quantity * this.price;
}
} |
Beta Was this translation helpful? Give feedback.
-
// Order 클래스
// calculateBasePrice() 삭제
//..생략..
calculateTotal() {
//..생략..
total += item.item.calculateBasePrice(item.quantity, item.item.price);
//..생략..
// OrderItem 클래스
//..생략..
calculateBasePrice(quantity, itemPrice) {
return quantity * itemPrice;
}
//..생략..
calculateShoppingCost(total) {
return total > 50000 ? 0 : 5000;
}
이거 맞는지 모르겠습니다. Order 클래스 외부로 이동하라는게 뭘까요... // 할인 계산 로직을 외부 함수로 이동
calculateDiscount(total) {
if (this.customer.isPremium) {
this.customer.discountRate = 0.1;
} else {
this.customer.discountRate = 0.05;
}
return total * (1 - this.customer.discountRate);
}
calculateTotal() {
let total = 0;
for (const item of this.items) {
if (item.item.stock < item.quantity) {
throw new Error('재고가 부족합니다.');
}
total += item.item.calculateBasePrice(item.quantity, item.item.price);
}
let shippingCost = this.calculateShoppingCost(total);
total = this.calculateDiscount(total);
return total + shippingCost;
}
checkStock(item) {
if (item.item.stock < item.quantity) {
throw new Error('재고가 부족합니다.');
}
}
calculateTotal() {
let total = 0;
for (const item of this.items) {
this.checkStock(item);
total += item.item.calculateBasePrice(item.quantity, item.item.price);
}
let shippingCost = this.calculateShoppingCost(total);
total = this.calculateDiscount(total);
return total + shippingCost;
}
// Order 클래스
//..생략..
calculateTotal() {
let total = 0;
for (const item of this.items) {
this.checkStock(item);
total += item.item.calculateBasePrice(item.quantity, item.item.price);
}
let shippingCost = this.calculateShoppingCost(total);
total = this.customer.calculateDiscount(total);
return total + shippingCost;
}
}
class Customer {
constructor(name, isPremium) {
this.name = name;
this.isPremium = isPremium;
this.discountRate = 0; //필드 옮기기!
}
calculateDiscount(total) {
this.discountRate = this.isPremium ? 0.1 : 0.05;
return total * (1 - this.discountRate);
}
}
// Before
for (const item of this.items) {
this.checkStock(item);
total += item.item.calculateBasePrice(item.quantity, item.item.price);
}
// After
for (const item of this.items) {
this.checkStock(item);
}
for (const item of this.items) {
total += item.item.calculateBasePrice(item.quantity, item.item.price);
} 완성...// 주문 처리 시스템 예제
class Order {
constructor(customer) {
this.customer = customer;
this.items = [];
}
addItem(item, quantity) {
this.items.push({
item: item,
quantity: quantity,
});
}
calculateShoppingCost(total) {
return total > 50000 ? 0 : 5000;
}
checkStock(item) {
if (item.item.stock < item.quantity) {
throw new Error('재고가 부족합니다.');
}
}
calculateTotal() {
let total = 0;
for (const item of this.items) {
this.checkStock(item);
}
for (const item of this.items) {
total += item.item.calculateBasePrice(item.quantity, item.item.price);
}
let shippingCost = this.calculateShoppingCost(total);
total = this.customer.calculateDiscount(total);
return total + shippingCost;
}
}
class Customer {
constructor(name, isPremium) {
this.name = name;
this.isPremium = isPremium;
this.discountRate = 0; //필드 옮기기!
}
calculateDiscount(total) {
this.discountRate = this.isPremium ? 0.1 : 0.05;
return total * (1 - this.discountRate);
}
}
class OrderItem {
constructor(name, price, stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
calculateBasePrice(quantity, itemPrice) {
return quantity * itemPrice;
}
}
const customer = new Customer('제니', true);
const order = new Order(customer);
order.addItem(new OrderItem('아이폰', 1000000, 10), 1);
order.addItem(new OrderItem('아이패드', 2720000, 10), 2);
console.log(order.calculateTotal()); |
Beta Was this translation helpful? Give feedback.
-
// ShoppingCart(user, calculateItemTotal, addItem, calculateTotal, checkStock, getProductStock, updateUserInterface)
// User(name, membership), Product(id, name, price)
class ShoppingCartPolicy {
constructor() {
this.maxItemCount = 20;
}
}
class ShoppingCartHistory {
constructor() {
this.lastUpdated = new Date();
}
setLastUpdated() {
this.lastUpdated = new Date();
}
}
class CartItem {
constructor() {}
calculateItemTotal(item) {
let total = item.price * item.quantity;
if (item.quantity > 10) {
total *= 0.9; // 10개 이상 구매시 10% 할인
}
return total;
}
}
class CartValidator {
constructor() {
this.policy = new ShoppingCartPolicy()
}
validate(items) {
if (items.length >= this.policy.maxItemCount) {
throw new Error("장바구니 최대 용량 초과");
}
}
}
class ShoppingCart {
constructor(user) {
this.user = user;
this.items = [];
this.history = new ShoppingCartHistory()
this.validator = new CartValidator()
}
existItem(productId) {return this.items.find(i => i.productId === productId);}
addItem(product, quantity) {
this.validator.validate(this.items)
if (this.existItem(product.id)) {
this.existItem(product.id).quantity += quantity;
} else {
this.items.push({
productId: product.id,
price: product.price,
quantity: quantity
});
}
// 단계 쪼개기 대상: 장바구니 업데이트 처리를 여러 단계로 분리 가능
this.history.setLastUpdated();
this.calculateTotal();
this.updateUserInterface();
}
calculateTotal() {
let total = this.items.reduce((item, p) => item + this.calculateItemTotal(p), 0)
this.items.forEach((item) => !this.checkStock(item) && console.warn(`재고 부족: ${item.productId}`);)
// 문장 슬라이드하기 대상: 할인 관련 코드를 한곳으로 모을 수 있음
if (this.user.membership === 'PREMIUM') {
total *= 0.95; // 프리미엄 회원 5% 추가 할인
}
// 배송비 계산
if (total < 30000) {
total += 3000;
}
return total;
}
checkStock(item) {
return item.quantity <= this.getProductStock(item.productId);
}
getProductStock(productId) {
// 재고 DB 조회 로직
return 100; // 예시 값
}
updateUserInterface() {
// UI 업데이트 로직
console.log("장바구니 UI 업데이트됨");
}
}
class User {
constructor(name, membership) {
this.name = name;
this.membership = membership;
}
}
class Product {
constructor(id, name, price) {
this.id = id;
this.name = name;
this.price = price;
}
} |
Beta Was this translation helpful? Give feedback.
-
PR 링크완성 코드export class Order {
constructor(customer) {
this.customer = customer;
this.items = [];
}
addItem(item, quantity) {
this.items.push({ item, quantity });
}
calculateTotal() {
this.#validateStock();
const total = this.#calculateBaseTotalPrice();
const shippingCost = this.#calculateShippingCost(total);
return this.customer.discount(total) + shippingCost;
}
#calculateShippingCost(total) {
if (total > 50000) return 0;
return 5000;
}
#calculateBaseTotalPrice() {
return this.items.reduce(
(total, { item, quantity }) =>
(total += item.calculateBasePrice(quantity)),
0
);
}
#validateStock() {
this.items.forEach((item) => {
if (item.item.stock < item.quantity) {
throw new Error("재고가 부족합니다.");
}
});
}
}
export class Customer {
constructor(name, isPremium) {
this.name = name;
this.isPremium = isPremium;
this.discountRate = this.#calculateDiscountRate();
}
#calculateDiscountRate() {
if (this.isPremium) return 0.1;
return 0.05;
}
discount(price) {
return price * (1 - this.discountRate);
}
}
export class OrderItem {
constructor(name, price, stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
calculateBasePrice(quantity) {
return quantity * this.price;
}
} |
Beta Was this translation helpful? Give feedback.
-
class Order {
constructor(customer) {
this.customer = customer;
this.items = [];
}
getDiscountRate() {
return this.customer.discountRate;
}
addItem(product, quantity) {
this.items.push({
product: product,
quantity: quantity
});
}
_calculateItemTotal() {
let total = 0;
for (const order of this.items) {
total += order.product.calculateBasePrice(order.quantity);
}
return total;
}
calculateTotal() {
let total = this._calculateItemTotal();
let shippingCost = total > 50000 ? 0 : 5000;
const discountRate = this.customer.discountRate || (this.customer.isPremium ? 0.1 : 0.05);
total *= (1 - discountRate);
return total + shippingCost;
}
}
class Customer {
constructor(name, isPremium, discountRate) {
this.name = name;
this.isPremium = isPremium;
this.discountRate = discountRate;
}
}
class OrderItem {
constructor(name, price, stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
calculateBasePrice(quantity) {
if (quantity > this.stock) {
throw new Error('재고가 부족합니다.');
}
return this.price * quantity;
}
} 다완료하지 못했지만ㅠ 리팩토링한 부분까진 올립니다!
|
Beta Was this translation helpful? Give feedback.
-
수정 중입니다! class Customer {
constructor(isPremium) {
this.isPremium = isPremium;
this.discountRate = isPremium ? 0.1 : 0;
}
}
class Order {
constructor(customer) {
this.customer = customer;
this.items = [];
}
addItem(item, quantity) {
this.items.push(new OrderItem(item, quantity));
}
calculateTotal() {
this.checkStock();
let total = this.calculatePrices();
total = this.applyDiscount(total);
total += this.calculateShippingCost(total);
return total;
}
checkStock() {
for (const orderItem of this.items) {
if (orderItem.item.stock < orderItem.quantity) {
throw new Error('재고가 부족합니다.');
}
}
}
calculatePrices() {
return this.items.reduce((total, orderItem) => total + orderItem.calculateBasePrice(), 0);
}
applyDiscount(total) {
const discount = total * this.customer.discountRate;
return total - discount;
}
calculateShippingCost(total) {
if (total > 50000) {
return 0;
}
return 5000;
}
}
class OrderItem {
constructor(item, quantity) {
this.item = item;
this.quantity = quantity;
}
calculateBasePrice() {
return this.quantity * this.item.price;
}
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
아, 죄송합니다! 리팩터링 2판의 8장 "기능 이동" 챕터에서 다루는 내용들(함수 옮기기, 필드 옮기기, 문장을 함수로 옮기기, 문장을 호출한 곳으로 옮기기, 인라인 코드를 함수 호출로 바꾸기, 문장 슬라이드하기, 반복문 쪼개기, 단계 쪼개기)을 종합적으로 포함하는 예제를 만들어보겠습니다:
이 코드는 다음과 같은 리팩터링 기회를 제공합니다:
함수 옮기기:
calculateBasePrice
메서드를OrderItem
클래스로 이동필드 옮기기:
discountRate
필드를Customer
클래스로 이동문장을 함수로 옮기기: 배송비 계산 로직을 별도의
calculateShippingCost
함수로 추출문장을 호출한 곳으로 옮기기: 할인 적용 로직을
Order
클래스 외부로 이동인라인 코드를 함수 호출로 바꾸기: 재고 확인 로직을
checkStock
함수로 추출문장 슬라이드하기: 할인율 계산 로직을 관련된 코드 근처로 이동
반복문 쪼개기: 상품 가격 계산과 재고 확인을 별도의 반복문으로 분리
단계 쪼개기: 전체 주문 처리 로직을 여러 단계(재고 확인, 가격 계산, 할인 적용, 배송비 계산)로 분리
각각의 리팩터링을 적용하면 코드의 응집도가 높아지고, 각 기능이 더 적절한 위치로 이동하며, 전체적인 코드 구조가 개선될 수 있습니다.
Beta Was this translation helpful? Give feedback.
All reactions