Springコア原理TIL(3)
[参考講座]金英漢のスプリングコア原理-基本編
会員 会員の登録と照会ができます. 会員には普通とVIPの2つの等級があります. 会員データは、独自のDBを構築したり、外部システムと連動したりすることができます.(保留中) オーダーおよび割引ポリシー 会員は商品を注文することができます. 会員等級によって割引政策が適用されます. 割引政策は、すべてのVIPに1000ウォンの固定金額割引を要求した. 割引政策が変わる可能性が高い.(保留中)
クライアント->メンバー・サービス->メモリ・メンバー・リポジトリ
列挙タイプと呼びます.
つまり、限定された値があれば、このタイプを使うことができます.
例えば月曜日火曜日水曜日木曜日のような日
デバイスがまだ特定されていないため、メモリ・メンバー・リポジトリをテストします.
会員登録ホームページ
前回の授業で学んだ内容と同じです.
OCP、DIPの原則に合致しない
😱 依存関係はインタフェースだけでなく,実装体にも依存する.
1.クライアントは、受注サービスによって受注を生成します.
2.注文サービスは会員リポジトリで会員を調べ、等級を知る.
3.注文サービスは等級を調べるため、割引政策により割引を実施する
4、注文サービスは割引を適用した結果とともに注文結果を返品する.
上記の設計では、注文サービスを変更することなく、メンバーのメモリ内のクエリー方式をDBクエリー方式に変更しました.
割引ポリシーを変更しても、オーダーサービスを変更する必要はありません.
提携関係をそのまま再利用できます.😎
toString形式で出力!
新しい割引政策を拡張します.
既存の政策がVIPクラスだと1000元の定額割引しかしないなら、
金額割引の定率割引に変更します.
割引ポリシーをアプリケーションに適用すると問題が発生します.
クライアントOrderServiceImplコードを修正する必要があります.のキャラクターを完全に分離します. 多形性を用いて,インタフェースと実装オブジェクトを分離した. しかし、このコードもOCP、DIPのオブジェクト向け設計の原則を守っていない. DIPの問題:
OrderServiceImplはDiscountPolicyインタフェースと実装クラスにも依存します. OCPの問題:
コードがクライアントコードに影響を及ぼし、機能を拡張および変更します.
次の図は、OCP、DIPに違反していることを明確に示しています.
DIPに違反しないように抽象のみに依存するように変更
=設計をインタフェースのみに依存するように変更
実際には、このコードを実行するとNull pointエラーが発生します.
実装体がないため、 を実行することができない.
ソリューション
この問題を解決するには、OrderServiceImplにDiscountPolicyインプリメンテーションを作成して注入する必要があります.次のレッスンから、上記のソリューションの内容を詳しく説明します.😎
💡 サンプルの作成
📍 ビジネスニーズと設計
📍 メンバードメイン設計
メンバードメインのコラボレーション
会員等級図
メンバー・オブジェクトのグラフ
회원 서비스 : MemberServiceImpl
メンバー・サービス・インタフェースが実装されました.慣例に従ってImplを貼り付けます.📍 メンバードメインの開発
会員等級
package hello.core.member;
public enum Grade {
BASIC,
VIP
}
💡 Enumデータ型とは?
列挙タイプと呼びます.
つまり、限定された値があれば、このタイプを使うことができます.
例えば月曜日火曜日水曜日木曜日のような日
メンバーエンティティ
public class Member {
private Long id;
private String name;
private Grade grade;
public Member(Long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
// 이후는 getter and setter code
メンバー・リポジトリ・インタフェース
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
メモリメンバーストレージインプリメンテーション
デバイスがまだ特定されていないため、メモリ・メンバー・リポジトリをテストします.
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
会員サービスインタフェース
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
会員サービス実施体
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository = new
MemoryMemberRepository();
public void join(Member member) {
memberRepository.save(member);
}
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
メンバードメインの実行とテスト
会員登録ホームページ
public class MemberApp {
public static void main(String[] args) {
MemberService memberService = new MemberServiceImpl();
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find Member = " + findMember.getName());
}
}
JUnitテストclass MemberServiceTest {
MemberService memberService = new MemberServiceImpl();
@Test
void join() {
//given
Member member = new Member(1L, "memberA", Grade.VIP);
//when
memberService.join(member);
Member findMember = memberService.findMember(1L);
//then
Assertions.assertThat(member).isEqualTo(findMember);
}
}
📍メンバードメイン設計における問題
前回の授業で学んだ内容と同じです.
OCP、DIPの原則に合致しない
😱 依存関係はインタフェースだけでなく,実装体にも依存する.
📍 オーダーと割引ドメインの設計
ドメインコラボレーション、ロール、ロールの管理
1.クライアントは、受注サービスによって受注を生成します.
2.注文サービスは会員リポジトリで会員を調べ、等級を知る.
3.注文サービスは等級を調べるため、割引政策により割引を実施する
4、注文サービスは割引を適用した結果とともに注文結果を返品する.
オーダードメインクラス図
上記の設計では、注文サービスを変更することなく、メンバーのメモリ内のクエリー方式をDBクエリー方式に変更しました.
割引ポリシーを変更しても、オーダーサービスを変更する必要はありません.
提携関係をそのまま再利用できます.😎
📍 発注および割引ドメインの開発
割引ポリシーインタフェース
public interface DiscountPolicy {
/**
* @return 할인 대상 금액
*/
int discount(Member member, int price);
}
固定割引政策実施体
public class FixDiscountPolicy implements DiscountPolicy {
private int discountFixAmount = 1000; //1000원 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) {
return discountFixAmount;
} else {
return 0;
}
}
}
等級VIPは1000元割引の政策です.受注エンティティ
public class Order {
private Long memberId;
private String itemName;
private int itemPrice;
private int discountPrice;
public Order(Long memberId, String itemName, int itemPrice, int
discountPrice) {
this.memberId = memberId;
this.itemName = itemName;
this.itemPrice = itemPrice;
this.discountPrice = discountPrice;
}
//이후 getter setter code
@Override
public String toString() {
return "Order{" +
} }
"memberId=" + memberId +
", itemName='" + itemName + '\'' +
", itemPrice=" + itemPrice +
", discountPrice=" + discountPrice +
'}';
}
}
TOString()を使用してオブジェクトを返す場合toString形式で出力!
オーダーサービスインタフェース
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
オーダーサービス実施機関
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
注文するときは、会員情報を照会し、等級を理解し、割引ポリシーを適用し、注文オブジェクトを生成して返します.発注および割引ドメインの実行
public class OrderApp {
public static void main(String[] args) {
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("order = " + order);
}
}
テスト
class OrderServiceTest {
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
@Test
void createOrder() {
long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
すべてのテストで青信号が点灯しました😎📍 新しい割引ポリシーの作成
新しい割引政策を拡張します.
既存の政策がVIPクラスだと1000元の定額割引しかしないなら、
金額割引の定率割引に変更します.
RateDiscountPolicyの追加
RateDiscountPolicyコードの追加
public class RateDiscountPolicy implements DiscountPolicy {
private int discountPercent = 10; //10% 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) {
return price * discountPercent / 100;
} else {
return 0;
}
}
}
テスト
class RateDiscountPolicyTest {
RateDiscountPolicy discountPolicy = new RateDiscountPolicy
@Test
@DisplayName("VIP는 10% 할인이 적용되어야 한다.")
void vip_o() {
//given
Member member = new Member(1L, "memberVIP", Grade.VIP);
//when
int discount = discountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(1000);
}
@Test
@DisplayName("VIP가 아니면 할인이 적용되지 않아야 한다.")
void vip_x() {
//given
Member member = new Member(2L, "memberBASIC", Grade.BASIC);
//when
int discount = discountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(0);
}
}
📍 新しい割引ポリシーと問題の適用
割引ポリシーをアプリケーションに適用すると問題が発生します.
クライアントOrderServiceImplコードを修正する必要があります.
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
に質問
OrderServiceImplはDiscountPolicyインタフェースと実装クラスにも依存します.
コードがクライアントコードに影響を及ぼし、機能を拡張および変更します.
ポリシーの変更
次の図は、OCP、DIPに違反していることを明確に示しています.
📍 トラブルシューティング方法
DIPに違反しないように抽象のみに依存するように変更
=設計をインタフェースのみに依存するように変更
コードをインタフェースのみに依存するように変更
public class OrderServiceImpl implements OrderService {
//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private DiscountPolicy discountPolicy;
実装体がないため、
ソリューション
この問題を解決するには、OrderServiceImplにDiscountPolicyインプリメンテーションを作成して注入する必要があります.次のレッスンから、上記のソリューションの内容を詳しく説明します.😎
Reference
この問題について(Springコア原理TIL(3)), 我々は、より多くの情報をここで見つけました https://velog.io/@yulhee741/Spring-핵심-원리-TIL-3テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol