본문 바로가기
James Cooper - Python Programming with D

The Factory Method Pattern

by 자동매매 2023. 3. 23.

5 장에서 우리는 가장 단순한 공장의 가지 예를 보았습니다. 팩토리 개념은 객체 지향 프로그래밍 전체에서 반복됩니다. 이러한 경우 단일 클래스는 교통 경찰 역할을 하며 인스턴스화할 단일 계층의 하위 클래스를 결정합니다.

 

Factory Method 패턴은 Factory pattern의 확장으로, 단일 클래스가 인스턴스화할 하위 클래스를 결정하지 않습니다. 대신 슈퍼클래스는 각 서브클래스에 대한 결정을 연기합니다. 이 패턴에는 실제로 하나의 하위 클래스가 다른 클래스보다 직접 선택되는 결정 지점이 없습니다. 대신 이 패턴으로 작성된 프로그램은 객체를 생성하지만 각 하위 클래스가 생성할 객체를 결정하도록 하는 추상 클래스를 정의합니다.

 

straight seeding

1) Heat 배정 방식 - 기록 오름 차순

2) line 배정 방식 - 중앙 -> 외곽

# Heat당 최소인원 : 3명

 

circle seeding

1) Heat 배정 방식 - 기록 오름 차순 + 상위 3개 Heat에 대해서만 별도 배정

2) line 배정 방식 - 기본: Straight Seeding, 상위 3개 Heat : 최상위 Heat -> 차상위 Heat -> 차차상위 Heat 순

 

So, how do we build some objects to implement this seeding scheme and illustrate the Factory Method as shown in Figure 6-1?

 

 

Figure 6-1 Factory Method pattern

 

 

First, let’s design an abstract Event class:

class Event():

    #place holders to be filled in in actual classes
    def getSeeding(self): pass
    def isPrelim(self): pass
    def isFinal(self): pass
    def isTimedFinal(self): pass

혼란을 피하기 위해 pass 문을 같은 줄에 배치하는 단순화에 유의하십시오.

 

이것은 메소드를 채울 필요없이 간단하게 메소드를 정의합니다. 그런 다음 PrelimEvent TimedFinalEvent라는 Event 클래스에서 구체적인 클래스를 파생시킬 있습니다. 이러한 클래스 간의 유일한 차이점은 하나는 특정 종류의 시드를 반환하고 다른 클래스는 다른 종류의 시드를 반환한다는 것입니다.


또한 다음 메서드를 갖는 Seeding 클래스를 정의합니다.
 
class Seeding:
    def getSwimmers(self): pass

 

그런 다음 개의 구체적인 시드 하위 클래스인 StraightSeeding CircleSeeding 만들 있습니다.

PrelimEvent 클래스는 CircleSeeding 인스턴스를 반환하고 TimedFinalEvent 클래스는 StraightSeeding 인스턴스를 반환합니다. 따라서 우리는 개의 계층 구조가 있음을 있습니다 : 이벤트와 시드 계층

 

이벤트 계층 구조에서 파생된 이벤트 클래스에는 모두 getSeeding 메서드가 포함되어 있습니다. 하나는 StraightSeeding 인스턴스를 반환하고 다른 하나는 CircleSeeding 인스턴스를 반환합니다. 보시다시피 간단한 예에서와 같이 실재 factory 결정 지점은 없습니다. 대신, 인스턴스화할 Event 클래스에 대한 결정은 인스턴스화될 seeding 클래스를 결정하는 것입니다.

클래스 계층 간에 대응있는 것처럼 보이지만 반드시 그럴 필요는 없습니다. 많은 종류의 이벤트가 있을 있으며 그들이 사용하는 시드 종류 가지뿐입니다.

 

The Swimmer Class

swimmer정보를 담는다.

Event에서 getSeeding method를 호출할 때 Seeding class에 Swimmer List를 전달한다.

 

Here it is in brief:

 

class Swimmer():
     def __init__(self, dataline):
         sarray = dataline.split()  #read in a row and separate the columns
         self.frname=sarray[1]      #names
         self.lname=sarray[2]
         self.age=int(sarray[3])    #age
         self.club=sarray[4]        #club symbol
         self.seedtime=sarray[5]    #seed time as string
         self.time=0.0              #set defaults
         self.lane=0                #seeded heats and lanes go here
         self.heat=0

    #mConcatenate first and last names
     def getName(self):
          return self.frname+" "+self.lname #combine names

 

The Event Classes

swimmer data (here from a file) -> Swimmer list : swimmer 분석 위해

class Event():
    def __init__(self, filename, lanes):
        self.numLanes = lanes
        self.swimmers=[]        #array of swimmers
        # read in the data file for this event
        f = open(filename, "r")
        # the Swimmer class parses each line of the data file
        for swstring in f:
            sw = Swimmer(swstring)
            self.swimmers.append(sw)
        f.close()

 

PrelimEvent class는CircleSeeding 인스턴스를 반환한다.

 

class PrelimEvent (Event):
 #creates a preliminary event which is circle seeded
    def __init__(self, filename, lanes):
        super().__init__(filename, lanes)

    def  getSeeding(self):
        return CircleSeeding(self.swimmers, self.numLanes)

 

TimedFinalEvent는 StraightSeeding 인스턴스를 반환한다.

 

class TimedFinalEvent (Event):
#creates an event that will be straight seeded
   def __init__(self, filename,  lanes):
        super().__init__(filename, lanes)

   def getSeeding(self):
        return StraightSeeding(self.swimmers, self.numLanes)

 

Straight Seeding

이 곳에서 주요 작업이 이루어진다.

CircleSeeding class는 StraightSeeding class을 상속한다 - swimmers list,  lane수 복사 기능

class StraightSeeding(Seeding):
    def __init__(self, sw, nlanes):
        self.swimmers = sw
        self.numLanes = nlanes
        self.count = len(sw)
        self.lanes = self.calcLaneOrder()
        self.seed()

 

basic seeding

 

    def seed(self):
    #loads the swmrs array and sorts it
        asw = self.sortUpwards()  # number in last heat
        self.lastHeat = self.count % self.numLanes
        if (self.lastHeat < 3):
            self.lastHeat = 3 # last heat must have 3 or more

        lastLanes =self.count - self.lastHeat
        self.numHeats = self.count / self.numLanes

        if (lastLanes > 0):
            self.numHeats += 1 # compute total number of heats
        heats = self.numHeats

        # place heat and lane in each swimmer's object
        j = 0
          # load from fastest to slowest
          # so we start with last heat  # and work downwards
        for i in range(0, lastLanes) :
            sw = asw[i] # get each swimmer
            sw.lane= int(self.lanes[j]) # copy in lane
            j += 1
            sw.heat = int(heats) # and heat
            if (j >= self.numLanes):
                heats -= 1 # next heat
                j=0
    # Add in last partial heat
        if (j < self.numLanes):
             heats -= 1
             j = 0
      #for (int i = lastLanes-1; i < count; i++):
        for i in range(lastLanes-1, self.count):
             sw = asw[i]
             sw.lane= int(self.lanes[j])
             j += 1
             sw.heat= int(heats)

    # copy from array back into list
        swimmers = []
        for i in range(0, self.count):
            swimmers.append(asw[i]);

getSwimmers method를 호출시 사용가능한 시드를 만든다.

 

 

Circle Seeding

 

The CircleSeeding class is derived from StraightSeeding, so it copies in the same data.

 

"""Circle seeding distributes the fastest swimmers into the top 3 heats"""
class CircleSeeding(StraightSeeding):
    def __init__(self, sw, nlanes):
        super().__init__(sw, nlanes)

    def seed(self):
        super().seed() # do straight seed as default
        if (self.numHeats >= 2):
            if (self.numHeats >= 3):
                circle = 3
            else:
                circle = 2
        i = 0

        for j in range(0, self.numLanes)    :
            for k in range(0, circle):
                self.swimmers[i].lanes = int(self.lanes[j])
                self.swimmers[i].heat = int(self.numHeats - k)
                i += 1

 

Because the constructor calls the parent class constructor, it copies the swimmer vector and lanes values. Then our call to super.seed() does the straight seeding. This simplifies programming because we will always need to seed the remaining heats by straight seeding. Then we seed the last two or three heats as shown above and we are done with that type of seeding as well.

 

Our Seeding Program

 

In this example, we took a list of swimmers who competed in the 500-yard freestyle and the 100-yard freestyle and used them to build our TimeFinalEvent and PrelimEvent classes.

The code for calling these two seedings is pretty simple. The console version enables you to enter 1 or 5 or 0 (to quit).

 

from SwimClasses import TimedFinalEvent, PrelimEvent
import sys

class Builder():

    def build(self):
        dist=1
        while dist > 0:
            dist = int(input('Enter 1 for 100, 5 for 500 or 0 to quit: '))
            if dist==1 or dist ==5:
                self.evselect(dist)

    # seed selected event
    def evselect(self, dist):
        # there are only two swimmer files
        # We read in one or the other

        if dist == 5 :
            event = TimedFinalEvent("500free.txt", 6)
        elif dist ==1:
            event = PrelimEvent("100free.txt", 6)

        seeding = event.getSeeding()    #factory here
        swmrs= seeding.getSwimmers()    #do one sort of seeding or the other

        #print swimmer list in seeded order
        for sw in swmrs:
           print(f'{sw.heat:3}{sw.lane:3} {sw.getName():20}{sw.age:3} {sw.seedtime:9}')

# -----main begins here----
def main():
    builder = Builder()
    builder.build()

###  Here we go  ####
if __name__ == "__main__":
    main()

 

You can see the results of these two seedings:

 

We also built a visual display, but you don’t have to use it. Since both the console and the GUI versions use the same classes, we put them all in a separate file, SwimClasses.py, and told the main program to import the two class references it uses:

The files just need to be in the same directory.

 

Other Factories

One issue that we have skipped over is how the program that reads in the swimmer data decides which kind of event to generate. We finesse this here by simply calling the two constructors directly:

 

 

Clearly, this is an instance where an EventFactory may be needed to decide which kind of event to generate. This revisits the simple factory we began the discussion with.

 

 

tesy.py
0.01MB

 

 

 

When to Use a Factory Method

You should consider using a Factory method in these cases:

  • 클래스는 만들어야 하는 개체의 종류를 예상할 없습니다.
  • 클래스는 하위 클래스를 사용하여 만들어질 개체를 지정합니다.
  • 어떤 클래스가 만들어지는지에 대한 정보를 지역화하려고 합니다.

There are several similar variations on the factory pattern to recognize:

  1. 기본 클래스는 추상적이며 패턴은 완전한 작업 클래스를 반환해야 합니다.
  2. 기본 클래스는 기본 메서드를 포함하며 기본 메서드가 충분하지 않은 경우에만 서브클래싱됩니다.
  3. 매개 변수는 여러 클래스 유형 중 반환 할 클래스를 알려주는 팩토리로 전달됩니다. 이 경우 클래스는 동일한 메서드 이름을 공유하지만 상당히 다른 작업을 수행할 수 있습니다.

Programs on GitHub

  • SwimFactoryConsole.py: Console seeding program
  • SwimClasses.py: Classes used by all three versions
  • SwimFactory.py: Puts a list into Listbox
  • SwimFactoryTable.py: Puts a list into Treeview

100free.txt
0.00MB
500free.txt
0.00MB
SwimClasses.py
0.01MB
SwimFactory.py
0.00MB
SwimFactoryConsole.py
0.00MB
SwimFactoryTable.py
0.00MB

'James Cooper - Python Programming with D' 카테고리의 다른 글

The Singleton Pattern  (0) 2023.03.23
The Abstract Factory Pattern  (0) 2023.03.23
The Factory Pattern  (0) 2023.03.23
What Are Design Patterns?  (0) 2023.03.23
Visual Programming of Tables of Data  (0) 2023.03.23

댓글