「Spring」オブジェクト向け原則の適用-新しい割引ポリシー&「DI」
新しい割引ポリシーの作成
パーセント割引ポリシーの変更
VIPの場合は10%割引になります
public class RateDiscountPolicy implements DiscountPolicy{
private int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP){
return price * discountPercent / 100;
} else{
return 0;
}
}
}
TestCaseを実行します(Ctrl+Shift+Tキーを押すとTest Classを作成するショートカットキーが得られます)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
Assertions.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
Assertions.assertThat(discount).isEqualTo(0);
}
}
成功!
新しい割引ポリシーと問題の適用
新しい割引ポリシーを導入しようとしたり、変更しようとしたりすると、実際にDIP、OCPを守れないという問題が発生します!!
問題を発見する
3.OCPやDIPなどのオブジェクト向けの設計原則を遵守していますか?X
->クラスの依存関係を解析する場合は,抽象(インタフェース)だけでなく,特定の(実装)クラスにも依存する.
-抽象(インタフェース)依存:DiscountPolicy
-具体(実施)依存:FixDiscountPolicy、RateDiscountPolicy
-機能を変更すると、クライアントコードに影響します.OCP違反
DiscountPolicyインタフェースのみに依存していると思います.
よく見ると、OrderServiceImplはDiscountPolicyインタフェースだけではありません.
FixDiscountPolicy実装クラスにも依存します.つまりDIP違反(DIP:常に抽象に依存)
DIP違反のため、ポリシーが変更された場合
FixDiscountPolicyをRateDiscountPolicyに変更する場合は、OrderServiceImplのソースコードも同時に変更する必要があります.OCP違反
この問題はどのように解決しますか。
しかし、実装体はなく、コードをどのように実行しますか?
実際に実行するとNPEが生成されます.
ソリューション
この問題を解決するには、クライアントOrderServiceImpleにDiscountPolicyのインプリメンテーションオブジェクトを作成して注入する必要があります!!
注目点の分離
AppConfigの出現
アプリケーションの完全な動作を構成するには、実装オブジェクトの作成と接続を担当する個別の設定クラスを作成します.
MemberServiceImpl
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memderId) {
return memberRepository.findById(memderId);
}
}
AppConfigpublic class AppConfig {
public MemberService memberService(){
// 생성자 주입; 생성자를 만들어놓고 구현체를 주입시킴.
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
説明:まずAppConfigでアクセスし、誰かがMemberServiceを呼び出したときにインプリメンテーションを作成し、ジェネレータ注入インプリメンテーションを実行します.つまり.
依存注入(DI)-「注入依存関係」
DI:抽象(インタフェース)は直接呼び出し実装ではなく,外部(AppConfig)から依存関係を注入する.
AppConfigの実行
MemberApp
public class MemberApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
// MemberServiceImpl을 직접 호출하지 않음. 구현체는 모두 appConfig가 결정
MemberService memberService = appConfig.memberService();
// 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());
}
}
OrderApppublic class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
// MemberService memberService = new MemberServiceImpl(null);
// OrderService orderService = new OrderServiceImpl(null, null);
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);
System.out.println("order = "+order.calculatePrice());
}
}
MemberServiceTestpublic class MemberServiceTest {
MemberService memberService;
@BeforeEach
public void beforeEach(){
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
}
...
}
OrderServiceTestpublic class OrderServiceTest {
MemberService memberService;
OrderService orderService;
@BeforeEach
public void beforeEach(){
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
orderService = appConfig.orderService();
}
...
}
具体的なクラスに依存しない!!すべて外注に頼っている。
整理する
AppConfig再編成
改造前
public class AppConfig {
public MemberService memberService(){
// 생성자 주입; 생성자를 만들어놓고 구현체를 주입시킴.
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
:キャラクターの体現が見えない.改造後
public class AppConfig {
public MemberService memberService(){
// 생성자 주입; 생성자를 만들어놓고 구현체를 주입시킴.
return new MemberServiceImpl(memberRepository());
}
private MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
public DiscountPolicy discountPolicy(){
return new FixDiscountPolicy();
}
}
:役割の分業が明確で、一目瞭然です.:重複データは消去されました.
新しい構造と割引ポリシーの適用
AppConfigの出現は、アプリケーションを大量の使用領域とオブジェクトの作成と構成領域に分割します.
構成領域を変更するだけです!!
AppConfig
ここを変更するだけで...
Reference
この問題について(「Spring」オブジェクト向け原則の適用-新しい割引ポリシー&「DI」), 我々は、より多くの情報をここで見つけました https://velog.io/@lilys/Spring-객체지향원리-적용-새로운-할인정책-DIテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol