실례 분석 Ruby 디자인 모드 프로 그래 밍 에서 Strategy 정책 모드 의 사용

11956 단어 Ruby디자인 모드
오늘 당신 의 leader 는 흥 겹 게 당신 을 찾 았 습 니 다.당신 이 그 를 도와 작은 도움 을 줄 수 있 기 를 바 랍 니 다.그 는 지금 급히 회의 에 가 려 고 합 니 다.뭘 도와 드릴 까요?궁금 해?
그 는 당신 에 게 현재 당신들 프로젝트 의 데이터베이스 에 사용자 정보 표 가 한 장 있 는데,안에 매우 많은 사용자 의 데이터 가 저장 되 어 있 으 며,지금 은 사용자 정 보 를 선택적으로 조회 하 는 기능 을 완성 해 야 한다 고 말 했다.그 는 많은 사용자 이름 을 포함 하 는 배열 을 전달 할 것 이 라 고 말 했다.이 사용자 이름 에 따라 해당 하 는 데 이 터 를 모두 찾 아야 한다.
이 기능 은 매우 간단 하 잖 아,너 는 시원스럽게 승낙 했 잖 아.프로젝트 가 MySQL 데이터 베 이 스 를 사용 하기 때문에 다음 코드 를 빠르게 썼 습 니 다.

require 'mysql' 
 
class QueryUtil 
  def find_user_info usernames 
    @db = Mysql.real_connect("localhost","root","123456","test",3306); 
    sql = "select * from user_info where " 
    usernames.each do |user| 
      sql << "username = '" 
      sql << user 
      sql << "' or " 
    end 
    puts sql 
    result = @db.query(sql); 
    result.each_hash do |row| 
      #             
    end 
    #                ,     
  ensure 
    @db.close 
  end 
end 
들 어 오 는 사용자 이름 배열 에 따라 SQL 문 구 를 조합 한 다음 데이터베이스 에 해당 하 는 줄 을 찾 습 니 다.디 버 깅 을 위해 서 는 맞 춤 형 SQL 문 구 를 인쇄 했다.
그리고 이 방법 을 테스트 하기 위해 다음 코드 를 썼 습 니 다.

qUtil = QueryUtil.new 
qUtil.find_user_info ["Tom", "Jim", "Anna"] 
지금 테스트 코드 를 실행 해 보 세 요.프로그램 이 잘못 되 었 습 니 다.그래서 바로 인쇄 된 SQL 문 구 를 검 사 했 는데 과연 문 제 를 발견 했다.
select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna' or  
맞 춤 형 SQL 문 구 는 마지막 에 or 키 워드 를 하나 더 추가 하 였 습 니 다!for 순환 이 마지막 데이터 까지 실 행 될 때 or 를 추가 해 서 는 안 되 지만 코드 가 어리석게 마지막 데이터 에 or 키 워드 를 추가 하여 SQL 구문 문법 이 잘못 되 었 습 니 다.
이거 어 떡 하지?
있다!너 는 영광 이 번쩍 이 며 해결 방법 을 생각해 냈 다.SQL 문 구 를 조합 한 후에 마지막 or 이전의 위 치 를 캡 처 하면 되 지 않 겠 습 니까?그래서 당신 은 코드 를 다음 과 같이 바 꾸 었 습 니 다.

require 'mysql' 
 
class QueryUtil 
  def find_user_info usernames 
    @db = Mysql.real_connect("localhost","root","123456","test",3306); 
    sql = "select * from user_info where " 
    usernames.each do |user| 
      sql << "username = '" 
      sql << user 
      sql << "' or " 
    end 
    sql = sql[0 .. -" or ".length] 
    puts sql 
    result = @db.query(sql); 
    result.each_hash do |row| 
      #             
    end 
    #                ,     
  ensure 
    @db.close 
  end 
end 
String 의 하위 문자열 을 자 르 는 방법 을 사용 하여 마지막 or 이전 부분 만 가 져 옵 니 다.이렇게 테스트 코드 를 실행 하면 모든 것 이 정상 입 니 다.인쇄 된 SQL 문 구 는 다음 과 같 습 니 다.

select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna' 
자,완공!너 는 자신만만 하 다.
너의 leader 가 회 의 를 마치 고 와 서 너의 성 과 를 보 았 다.전체적으로 말 하면 그 는 매우 만 족 스 럽 지만 네가 사용 하 는 SQL 구문 조합 알고리즘 에 대해 그 는 항상 뭔 가 이상 하 다 고 느 꼈 지만 어디 가 나 쁜 지 말 할 수 없다.그래서 그 는 SQL 문 구 를 맞 추 는 또 다른 알고리즘 을 알려 주 었 다.코드 에 넣 으 라 고 했 지만 그 전의 알고리즘 도 삭제 하지 말고 남 겨 두 었 다가 다시 이야기 하 자 그 는 또 바 쁜 듯 이 달 아 났 다.그래서 당신 은 그 가 방금 가르쳐 준 알고리즘 을 넣 었 습 니 다.코드 는 다음 과 같 습 니 다.

require 'mysql' 
 
class QueryUtil 
  def find_user_info(usernames, strategy) 
    @db = Mysql.real_connect("localhost","root","123456","test",3306); 
    sql = "select * from user_info where " 
    if strategy == 1 
      usernames.each do |user| 
        sql << "username = '" 
        sql << user 
        sql << "' or " 
      end 
      sql = sql[0 .. -" or ".length] 
    elsif strategy == 2 
      need_or = false 
      usernames.each do |user| 
        sql << " or " if need_or 
        sql << "username = '" 
        sql << user 
        sql << "'" 
        need_or = true 
      end 
    end 
    puts sql 
    result = @db.query(sql); 
    result.each_hash do |row| 
      #             
    end 
    #                ,     
  ensure 
    @db.close 
  end 
end 
leader 가 가르쳐 준 맞 춤 법 을 볼 수 있 습 니 다.하나의 불 변 수 를 사용 하여 or 라 는 키 워드 를 추가 해 야 하 는 지 여 부 를 제어 합 니 다.처음으로 for 순환 을 실 행 했 을 때 이 불 값 은 false 이기 때문에 or 를 추가 하지 않 습 니 다.순환 의 마지막 에 불 값 을 true 로 할당 하면 나중에 순환 할 때마다 머리 에 or 키 워드 를 추가 합 니 다.머리 에 or 를 추가 하 는 방법 을 사 용 했 기 때문에 SQL 문장의 끝 에 or 가 하나 더 나 올 까 봐 걱정 하지 않 아 도 됩 니 다.그리고 당신 은 두 알고리즘 을 모두 보존 하기 위해 finduser_info 방법 에 매개 변 수 를 추가 하 였 습 니 다.strategy 값 은 1 로 첫 번 째 알고리즘 을 사용 하고 strategy 값 은 2 로 두 번 째 알고리즘 을 사용 하 는 것 을 표시 합 니 다.
이렇게 테스트 코드 도 다음 과 같은 방식 으로 바 꿔 야 한다.

qUtil = QueryUtil.new 
qUtil.find_user_info(["Tom", "Jim", "Anna"], 2) 
여기 서 두 번 째 알고리즘 을 사용 하여 SQL 문 구 를 조합 하 는 것 을 매개 변 수 를 통 해 밝 혔 습 니 다.인쇄 결 과 는 첫 번 째 알고리즘 을 사용 하 는 것 과 똑 같 습 니 다.
너 는 즉시 너의 leader 를 바 쁜 와 중 에 끌 고 와 서 그 에 게 너의 현재 성 과 를 검증 하 게 하 였 으 나,그 는 여전히 예전 과 같이 까다롭다.
"이렇게 쓰 면 finduser_info 이 방법의 논 리 는 너무 복잡 합 니 다.읽 기 에 매우 불리 하고 미래의 확장 에 도 불리 합 니 다.만약 에 제 가 세 번 째 네 번 째 알고리즘 을 추가 하고 싶다 면 이 방법 을 볼 수 있 습 니까?"  당신 의 leader 는 이러한 상황 에 직면 하면 전략 모델 로 해결 해 야 한다 고 지적 합 니 다.전략 모델 의 핵심 사상 은 바로 알고리즘 을 추출 하여 독립 된 대상 에 두 는 것 입 니 다.
너 를 지적 하기 위해 서 그 는 자신의 바 쁜 일 에 도 불구 하고 전략 모델 을 어떻게 사용 하여 최적화 하 는 지 가르쳐 주기 시작 했다.
먼저 부모 클래스 를 정의 합 니 다.부모 클래스 에는 get 이 포함 되 어 있 습 니 다.sql 방법,이 방법 은 간단하게 이상 을 던 지 는 것 입 니 다.

class Strategy 
  def get_sql usernames 
    raise "You should override this method in subclass." 
  end 
end 
그 다음 에 두 개의 하위 클래스 가 상기 부모 클래스 를 계승 하고 두 가지 SQL 문 구 를 조합 하 는 알고리즘 을 각각 두 개의 하위 클래스 에 추가 하도록 정의 합 니 다.

class Strategy1 
  def get_sql usernames 
    sql = "select * from user_info where " 
    usernames.each do |user| 
      sql << "username = '" 
      sql << user 
      sql << "' or " 
    end 
    sql = sql[0 .. -" or ".length] 
  end 
end 

class Strategy2 
  def get_sql usernames 
    sql = "select * from user_info where " 
    need_or = false 
    usernames.each do |user| 
      sql << " or " if need_or 
      sql << "username = '" 
      sql << user 
      sql << "'" 
      need_or = true 
    end 
  end 
end 

그리고 Query Util 에 있 는 finduser_info 방법 에서 Strategy 의 get 호출sql 방법 은 맞 춤 형 SQL 문 구 를 얻 을 수 있 습 니 다.코드 는 다음 과 같 습 니 다.

require 'mysql' 
 
class QueryUtil 
  def find_user_info(usernames, strategy) 
    @db = Mysql.real_connect("localhost","root","123456","test",3306); 
    sql = strategy.get_sql(usernames) 
    puts sql 
    result = @db.query(sql); 
    result.each_hash do |row| 
      #             
    end 
    #                ,     
  ensure 
    @db.close 
  end 
end 
마지막 으로 테스트 코드 는 find 를 호출 합 니 다.user_info 방법 은 어떤 정책 대상 을 사용 해 야 하 는 지 표시 하기 만 하면 됩 니 다.

qUtil = QueryUtil.new 
qUtil.find_user_info(["Tom", "Jim", "Anna"], Strategy1.new) 
qUtil.find_user_info(["Jac", "Joe", "Rose"], Strategy2.new) 
출력 된 SQL 문 구 는 예상 치 못 했 습 니 다.다음 과 같 습 니 다.

select * from user_info where username = 'Tom' or username = 'Jim' or username = 'Anna' 
select * from user_info where username = 'Jac' or username = 'Joe' or username = 'Rose' 
정책 모드 를 수정 한 후에 코드 의 가 독성 과 확장 성 이 크게 향상 되 었 습 니 다.나중에 새로운 알고리즘 을 추가 해 야 하 더 라 도 손 쉽게 잡 을 수 있 습 니 다!
전략 모드 와 간단 한 공장 모드 가 결 합 된 실례
필요:
백화점 의 수금 소프트웨어 는 고객 이 물건 을 구 매 하 는 단가 와 수량 에 따라 비용 을 계산 하면 판 촉 활동,20%할인,만 300 에서 100%할인 등 이 있 을 것 이다.
1.공장 모드 사용

# -*- encoding: utf-8 -*-

#       
class CashSuper
  def accept_cash(money)
  end
end

#      
class CashNormal < CashSuper
  def accept_cash(money)
    money
  end
end

#      
class CashRebate < CashSuper
  attr_accessor :mony_rebate
  
  def initialize(mony_rebate)
    @mony_rebate = mony_rebate
  end

  def accept_cash(money)
    money * mony_rebate
  end
end

#      
class CashReturn < CashSuper
  attr_accessor :mony_condition, :mony_return
  
  def initialize(mony_condition, mony_return)
    @mony_condition = mony_condition
    @mony_return = mony_return
  end

  def accept_cash(money)
    if money > mony_condition
      money - (money/mony_condition) * mony_return
    end
  end
end

#       
class CashFactory
  def self.create_cash_accept(type)
    case type
    when '    '
      CashNormal.new()
    when ' 8 '
      CashRebate.new(0.8)
    when '    100'
      CashReturn.new(300,100)
    end
  end
end

cash0 = CashFactory.create_cash_accept('    ')
p cash0.accept_cash(700)

cash1 = CashFactory.create_cash_accept(' 8 ')
p cash1.accept_cash(700)

cash2 = CashFactory.create_cash_accept('    100')
p cash2.accept_cash(700)

사용자 정의 할인 비율 과 만 감 수량 을 만 들 었 습 니 다.
존재 하 는 문제:
활동 의 종 류 를 늘 릴 때 는 50%를 할인 하고 500%를 채 우 면 200%를 줄 이 며 공장 류 에 분기 구 조 를 추가 해 야 한다.
이벤트 가 다양 하고 포인트 이벤트 가 늘 어 날 수도 있 습 니 다.100 포 인 트 를 채 우 고 10 포 인 트 를 더 하면 포 인 트 는 이벤트 상품 을 꼭 받 을 수 있 습 니 다.이때 하위 클래스 를 추가 해 야 합 니 다.
그러나 활동 을 늘 릴 때마다 공장 류 를 수정 하 는 것 은 나 쁜 처리 방식 이 므 로 알고리즘 이 바 뀌 었 을 때 더 좋 은 방법 이 있어 야 한다.
2.전략 모드
CashSuper 와 하위 클래스 는 변 하지 않 습 니 다.다음 내용 을 추가 합 니 다.

class CashContext
  
  attr_accessor :cs
  
  def initialize(c_super)
    @cs = c_super
  end
  
  def result(money)
    cs.accept_cash(money)
  end

end

type = ' 8 '
cs=case type
  when '    '
    CashContext.new(CashNormal.new())
  when ' 8 '
    CashContext.new(CashRebate.new(0.8))
  when '    100'
    CashContext.new(CashReturn.new(300,100))
  end
p cs.result(700)

CashContext 류 는 서로 다른 CashSuper 서브 클래스 를 봉인 하여 해당 하 는 result 로 되 돌려 줍 니 다.알고리즘 이 어떻게 변 하 든 서로 다른 알고리즘 을 봉인 한 것 이다.result 로 결 과 를 얻 을 수 있 습 니 다.
그러나 사용자 가 어떤 알고리즘 을 사용 할 지 판단 해 야 하 는 문제 가 있다.단순 공장 류 와 결합 할 수 있다.
3.전략 과 단순 공장 의 결합

class CashContext
  
  attr_accessor :cs
  
  def initialize(type)
    case type
    when '    '
      @cs = CashNormal.new()
    when ' 8 '
      @cs = CashRebate.new(0.8)
    when '    100'
      @cs = CashReturn.new(300,100)
    end
  end
  
  def result(money)
    cs.accept_cash(money)
  end

end

cs=CashContext.new(' 8 ')

p cs.result(700)

CashContext 에서 서로 다른 하위 클래스 를 예화 하 였 습 니 다.(단순 공장
하위 클래스 선택 과정 을 내부 로 옮 겨 알고리즘(전략 모드)을 패키지 합 니 다.
호출 자 는 더 간단 하고 매개 변수(활동 유형,원가)를 입력 하면 최종 결 과 를 얻 을 수 있 습 니 다.
여기 서 사용 자 는 한 가지 유형(Cash Context)만 알 면 되 고 간단 한 공장 에 서 는 두 가지 유형(Cash Factory 의 acceptcash 방법 과 Cash Factory),즉 더 철저하게 봉 인 된 것 이다.

좋은 웹페이지 즐겨찾기