OrderService
클래스가 Order
클래스의 내부 필드에 직접 접근하고 있다. 두 클래스 사이의 결합도는 높다고 볼 수 있다.public class Order {
public double price;
public int quantity;
public double getTotalPrice() {
return price * quantity;
}
}
public class OrderService {
public void printOrderTotalPrice(Order order) {
// 직접 Order 클래스의 내부 구조에 접근
System.out.println(order.price * order.quantity);
}
}
OrderService
클래스가 Order
클래스의 메서드만 호출하고, Order
클래스의 내부 구조나 로직에 의존하지 않는다면, 두 클래스 사이의 결합도는 낮다고 볼 수 있다.public class Order {
private double price;
private int quantity;
public double getTotalPrice() {
return price * quantity;
}
}
public class OrderService {
public void printOrderTotalPrice(Order order) {
// Order 클래스의 제공하는 메서드만 사용
System.out.println(order.getTotalPrice());
}
}
의도를 분명히 전달할 수 있는 이름 설계하기
변수를 계속해서 재할당 하는것이 아닌 목적별로 변수를 따로 생성하기
데이터와 로직이 묶여 있는 좋은 클래스
class OrderItem {
private String productName;
private double price;
private int quantity;
public OrderItem(String productName, double price, int quantity) {
if (price < 0) {
throw new IllegalArgumentException("Price cannot be negative");
}
if (quantity < 0) {
throw new IllegalArgumentException("Quantity cannot be negative");
}
this.productName = productName;
this.price = price;
this.quantity = quantity;
}
// 해당 품목의 총 가격을 계산
public double calculateTotalPriceForItem() {
return price * quantity;
}
}
calculateTotalPriceForItem
→ 메소드 명만 확인해도 해당 품목의 총 가격을 계산하는 로직인 걸 알수 있다.Private
, Public
, Protected
로 사용되며 일반적으로 캡슐화 원칙에 따라 private
로 설정되어 외부로부터 직접 접근이 제한된다.private
로 선언되었다면, 해당 변수를 직접 조작할 수 없다. 대신, 이 변수의 값을 조회하거나 변경하기 메서드를 사용한다.class OrderItem {
private String productName;
private double price;
private int quantity;
public OrderItem(String productName, double price, int quantity) {
if (price < 0) {
throw new IllegalArgumentException("Price cannot be negative");
}
if (quantity < 0) {
throw new IllegalArgumentException("Quantity cannot be negative");
}
this.productName = productName;
this.price = price;
this.quantity = quantity;
}
}
class OrderItem {
private final String productName;
private final double price;
private final int quantity;
}
class OrderItem {
private final String productName;
private final double price;
private final int quantity;
public OrderItem updatedQuantity(int newQuantity) {
return new OrderItem(productName, price, newQuantity);
}
}
class OrderItem {
private String productName;
private double price;
private int quantity;
}
class Order {
final OrderItem orderItem;
Order(OrderItem orderItem) {
this.orderItem = orderItem;
}
}
public class OrderServiceDemo {
public static void main(String[] args) {
OrderItem sharedOrderItem = new OrderItem("Laptop", 1000, 1);
Order order1 = new Order(sharedOrderItem);
Order order2 = new Order(sharedOrderItem);
// order1의 가격 변경
order1.orderItem.price(1200);
System.out.println("Order1 Total Price after: " + order1.OrderItem.price);
System.out.println("Order2 Total Price after: " + order2.OrderItem.price);
}
}
public class OrderItem {
private double price;
private int quantity;
private static double discount = 0.1; // 전역적인 할인율
public void calculateDiscountedPrice() { // 상태를 매개변수로 안 받는다.
this.price * (1 - discount); // 상태를 변경한다.
}
}
public class OrderItem {
private final double price; // 불변성을 위해 final 키워드 사용한다.
private final int quantity; // 불변성을 위해 final 키워드 사용한다.
public OrderItem(double price, int quantity) {
this.price = price;
this.quantity = quantity;
}
public double calculateDiscountedPrice(double discount) { // 상태는 매개변수로 받는다.
return price * (1 - discount); // 상태를 변경하지 않고, 같은 입력에 대해 항상 같은 값을 반환한다.
}
}
public OrderItem updateQuantity(final int newQuantity) {
return new OrderItem(this.price, newQuantity);
}
public class OrderItem {
private final double price;
private int quantity;
// 수량 변경 메서드 (가변성 도입)
public void updateQuantity(int newQuantity) {
this.quantity = newQuantity;
}
}
public class Order {
private List<OrderItem> orderItems;
// 응집도가 낮은 static 메소드
public static double calculateVAT(double price) {
return price * 0.1; // 부가세 10% 계산
}
}
// 인스턴스를 생성하지 않고 호출
double vatAmount = Order.calculateVAT(totalPrice);
public class Order {
private List<OrderItem> orderItems;
// 인스턴스 메소드인 척 하는 static 메소드
public double calculateDiscountedPrice(double originalPrice, double discountRate) {
return originalPrice * (1 - discountRate);
}
}
calculateDiscountedPrice
→ orderItems을 전혀 사용하지 않는 메소드이다.calculateVAT
와 거의 동일하다.public class OrderService {
public double applyDiscount(double orderAmount, double discountRate) {
if (orderAmount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
if (discountRate < 0) {
throw new IllegalArgumentException("discountRate cannot be negative");
}
return orderAmount * (1 - discountRate);
}
public double calculateVAT(double orderAmount) {
if (orderAmount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
return orderAmount * 0.1; // 10% VAT
}
}
public class OrderAmount {
private final double amount;
public OrderAmount(double amount) {
if (amount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
this.amount = amount;
}
}
// DiscountRate 클래스
public class DiscountRate {
private final double rate;
public DiscountRate(double rate) {
if (rate < 0 || rate > 1) { // 0 ~ 1 사이의 값만 유효하다고 가정
throw new IllegalArgumentException("Discount rate must be between 0 and 1");
}
this.rate = rate;
}
public double applyTo(double amount) {
return amount * (1 - rate);
}
}
// OrderService 클래스
public class OrderService {
private final OrderAmount orderAmount;
private final DiscountRate discountRate;
public OrderService(OrderAmount orderAmount, DiscountRate discountRate) {
this.orderAmount = orderAmount;
this.discountRate = discountRate;
}
public double applyDiscount() {
return discountRate.applyTo(orderAmount.getAmount());
}
public double calculateVAT() {
return orderAmount.getAmount() * 0.1;
}
}
public class Order {
private ShippingStatus shippingStatus;
public ShippingStatus getShippingStatus() {
return shippingStatus;
}
public void setShippingStatus(ShippingStatus status) {
this.shippingStatus = status;
}
}
public class OrderService {
public void shipOrder(Order order) {
// order 객체에게 묻고 있다.
if (order.getShippingStatus() == ShippingStatus.PENDING) {
order.setShippingStatus(ShippingStatus.SHIPPED);
}
}
}
public class Order {
private ShippingStatus shippingStatus;
public void ship() {
if (this.shippingStatus == ShippingStatus.PENDING) {
this.shippingStatus = ShippingStatus.SHIPPED;
}
}
}
public class OrderService {
public void shipOrder(Order order) {
// order 객체에게 묻는 것이 아닌 order 객체가 주체적으로 제어한다.
order.ship();
}
}
if (조건) {
// 수십 ~ 수백 줄의 코드
if (조건) {
// 수십 ~ 수백 줄의 코드
if (조건) {
// 수십 ~ 수백 줄의 코드
if (조건) {
// 수십 ~ 수백 줄의 코드
}
}
}
}
public class OrderService {
public String processOrder(Order order) {
if (!order.isPaid()) return "Order has not been paid!";
if (!order.isInStock()) return "Item is out of stock!";
if (!order.hasValidShippingAddress()) return "Invalid shipping address!";
order.ship();
return "Order has been shipped!";
}
}
public class OrderService {
public String processOrder(Order order) {
if (!order.isPaid()) return "Order has not been paid!";
if (!order.isInStock()) return "Item is out of stock!";
if (!order.hasValidShippingAddress()) return "Invalid shipping address!";
if (order.getItemCount() < 2) order.addShippingFee(); // 분기 요구사항
order.ship();
order.setFreeShipping(); // 비즈니스 요구사항
return "Order has been shipped!";
}
}
public class DiscountService {
public double applyDiscount(double orderAmount, String discountType) {
if ("PERCENT_10".equals(discountType)) {
return orderAmount * 0.9;
} else if ("PERCENT_20".equals(discountType)) {
return orderAmount * 0.8;
} else if ("FLAT_50".equals(discountType)) {
return orderAmount - 50;
}
return orderAmount;
}
}
interface DiscountStrategy {
double apply(double orderAmount);
}
class Percent10Discount implements DiscountStrategy {
public double apply(final double orderAmount) {
return orderAmount * 0.9;
}
}
class Percent20Discount implements DiscountStrategy {
public double apply(final double orderAmount) {
return orderAmount * 0.8;
}
}
class Flat50Discount implements DiscountStrategy {
public double apply(final double orderAmount) {
return orderAmount - 50;
}
}
public class DiscountService {
private DiscountStrategy strategy;
public DiscountService(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double applyDiscount(double orderAmount) {
return strategy.apply(orderAmount);
}
}
깨진 유리창 이론이란?
public class DiscountService {
private DiscountStrategy strategy;
public DiscountService(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double applyDiscount(double orderAmount) {
if (strategy instanceof Flat50Discount) {
orderAmount += 30;
}
return strategy.apply(orderAmount);
}
}
interface DiscountStrategy {
double apply(double orderAmount);
}
class Flat50Discount implements DiscountStrategy {
public double apply(final double orderAmount) {
return orderAmount - 50 + 30;
}
}
public class DiscountService {
private DiscountStrategy strategy;
public DiscountService(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double applyDiscount(double orderAmount) {
return strategy.apply(orderAmount);
}
}