Golang_designpatter_ebook_kth
Golang Design Pattern 예제
디자인 패턴에 대한 기본 설명은 https://yunhos.blogspot.com/2021/03/kotlindesignpatternebookkth.html 에서 참고.
Creational Patterns
Singleton
//count변수 하나만 가지고 있는 구조체
type singleton struct {
count int
}
var instance *singleton
func GetInstance() *singleton {
if instance == nil {
instance = new(singleton)
}
return instance
}
// 메소드 리시버로 구조체를 받아서 카운트를 증가하는 초간단로직의 싱글톤
func (s *singleton) AddOne() int {
s.count++
return s.count
}
test
import "testing"
func TestGetInstance(t *testing.T) {
counter1 := GetInstance()
if counter1 == nil {
t.Error("싱글톤 에러")
}
}
Factory
package factory
type iOrderWhat interface {
setName(name string)
getName() string
}
type factoryLine struct {
name string
}
func (factoryLine *factoryLine) setName(name string) {
factoryLine.name = name
}
func (factoryLine *factoryLine) getName() string {
return factoryLine.name
}
// 요소의 이름과 타입을 같이 지정하면 bmw.getName() 처럼 함축해서 기술할수 있다
type BMW struct {
factoryLine
}
func makeBMW() *BMW {
return &BMW{
factoryLine: factoryLine{
name: "BMW",
},
}
}
// struct 의 요소에 이름을 지정하면 audi.whichLine.getName() 처럼 호출해야한다.
type AUDI struct {
whichLine factoryLine
}
func makeAUDI() *AUDI {
return &AUDI{
whichLine: factoryLine{
name: "AUDI",
},
}
}
test
import "testing"
func TestFactory(t *testing.T) {
bmw := makeBMW()
if bmw.getName() != "BMW" {
t.Error("BMW 제조공장 에러")
}
audi := makeAUDI()
if audi.whichLine.getName() != "AUDI" {
t.Error("AUDI 제조공장 에러")
}
}
Abstract Factory
package abstractfactory
import "fmt"
//추상팩토리 인터페이스에서는 각 공장에서 OO를 만들자 라는 공통점을 가지도록한다.
type iAbstractFactory interface {
makeProduct() iVechicle
}
//탈것 형식에 맞게 생산한다.
func produceProduct(vechicleType string) (iAbstractFactory, error) {
if vechicleType == "car" {
return &carFactory{}, nil
}
if vechicleType == "airplane" {
return &airplaneFactory{}, nil
}
return nil, fmt.Errorf("그런거 못 만들어요")
}
// 자동차 공장
type carFactory struct {
}
// 탈것을 만들되 자동차공장 구조체를 통해서 각부품을 생산한다.
func (cf *carFactory) makeProduct() iVechicle {
return &vechicle{
factoring: factoring{
engineFactory: engineFactory{ power: "200MP" },
wheelFactory: wheelFactory{count: "4"},
wingFactory: wingFactory{wing: "0"},
},
}
}
type airplaneFactory struct {
}
func (cf *airplaneFactory) makeProduct() iVechicle {
return &vechicle{
factoring: factoring{
engineFactory: engineFactory{ power: "3200MP" },
wheelFactory: wheelFactory{count: "3"},
wingFactory: wingFactory{wing: "2"},
},
}
}
// 탈것이 공통으로 가질 속성을 정의한다.
type iVechicle interface {
setPower(power string)
getPower() string
setWheel(wheel string)
getWheel() string
setWing(wing string)
getWing() string
}
// 생산과정에서 엔진, 바퀴, 날개 각 공장을 통해 생산한다.
type factoring struct {
engineFactory
wheelFactory
wingFactory
}
// 탈것은 생산을 통해 만들어 진다.
type vechicle struct {
factoring
}
// 탈것의 공통속성에 대한 구현을 한다.
func (v vechicle) getPower() string {
return v.power
}
func (v vechicle) getWheel() string {
return v.count
}
func (v vechicle) getWing() string {
return v.wing
}
func (v vechicle) setPower(power string) {
v.power = power
}
func (v vechicle) setWheel(wheel string) {
v.count = wheel
}
func (v vechicle) setWing(wing string) {
v.wing = wing
}
//각 공장을 정의한다.
type engineFactory struct {
power string
}
type wheelFactory struct {
count string
}
type wingFactory struct {
wing string
}
test
package abstractfactory
import (
"testing"
)
func TestFactory(t *testing.T) {
produce, _ := produceProduct("car")
myCar := produce.makeProduct()
if myCar.getPower() != "200MP" {
t.Error("자동차 마력은 200MP")
}
produce2, _ := produceProduct("airplane")
myAirplain := produce2.makeProduct()
if myAirplain.getPower() != "3200MP" {
t.Error("비행기 마력은 3200MP")
}
}
Builder Factory
package builder
type iCoffeResources interface {
setCoffee(coffee int) iCoffeResources
setSugar(suger int) iCoffeResources
setCream(cream int) iCoffeResources
getResources() *coffeResource
}
type coffeResource struct {
coffe int
sugar int
cream int
}
func (coffeMix *coffeResource) getResources() *coffeResource {
return coffeMix;
}
func (coffeMixer *coffeResource) setCoffee(coffee int) iCoffeResources {
coffeMixer.coffe = coffee
return coffeMixer
}
func (coffeMixer *coffeResource) setSugar(sugar int) iCoffeResources {
coffeMixer.sugar = sugar
return coffeMixer
}
func (coffeMixer *coffeResource) setCream(cream int) iCoffeResources {
coffeMixer.cream = cream
return coffeMixer
}
// 기본 재료 세팅
func newCoffeBulder() iCoffeResources {
return &coffeResource{
coffe: 0,
sugar: 0,
cream: 0,
}
}
// 재료로 커피 생산 func produceCoffee(r iCoffeResources) coffeResource {
return coffeResource{
coffe: r.getResources().coffe,
sugar: r.getResources().sugar,
cream: r.getResources().cream,
}
}
func coffeMachine(coffeType string) coffeResource {
baseCoffee := newCoffeBulder()
if coffeType == "espresso" {
baseCoffee.setCoffee(2)
}
if coffeType == "mix" {
baseCoffee.setCoffee(1).setCream(1).setSugar(1)
}
return produceCoffee(baseCoffee)
}
test
package builder
import (
"testing"
)
func TestFactory(t *testing.T) {
coffeeMachine := coffeMachine("espresso")
if coffeeMachine.coffe != 2 {
t.Error("에소프레소는 커피2")
}
coffeeMachine = coffeMachine("mix")
if coffeeMachine.cream != 1 || coffeeMachine.sugar != 1 || coffeeMachine.coffe != 1{
t.Error("믹스 커피의 조합은 1:1:1")
}
}
Structural Patterns
Decorator Pattern
package decorator
type iHambugger interface {
getPrice() int
}
type hambugger struct {
price int
}
func (h *hambugger) getPrice() int {
return 1000
}
type meetTopping struct {
iHambugger
}
func (m *meetTopping) getPrice() int {
return m.iHambugger.getPrice() + 300
}
type eggTopping struct {
iHambugger
}
func (e *eggTopping) getPrice() int {
return e.iHambugger.getPrice() + 200
}
test
package decorator
import (
"testing"
)
func TestFactory(t *testing.T) {
baseBugger := &hambugger{}
meetOnlyBugger := &meetTopping{
iHambugger: baseBugger,
}
if meetOnlyBugger.getPrice() != 1300 {
t.Error("미트버거 계산오류")
}
eggOnlyBugger := &eggTopping{
iHambugger: baseBugger,
}
if eggOnlyBugger.getPrice() != 1200 {
t.Error("에그버거 계산오류")
}
meetEggBugger := &eggTopping{
iHambugger: meetOnlyBugger,
}
if meetEggBugger.getPrice() != 1500 {
t.Error("미트에그버거 계산오류")
}
}
Adaptor Pattern
package adaptor
type toTypeC interface {
connect() string
}
type usb1 struct {
}
func (u *usb1) connect() string {
return "usb1 연결"
}
type thunderbird struct {
}
func (u *thunderbird) connect() string {
return "썬더보드 연결"
}
type computer struct {
}
func (c *computer) connectCable(typeC toTypeC) string {
return typeC.connect()
}
test
package adaptor
import (
"testing"
)
func TestFactory(t *testing.T) {
computer := &computer{}
msg := computer.connectCable(&usb1{})
if msg != "usb1 연결" {
t.Error("usb1 연결오류")
}
msg = computer.connectCable(&thunderbird{})
if msg != "썬더보드 연결" {
t.Error("썬더보드 연결오류")
}
}
Bridge Pattern
package bridge
type mac struct {
printer
}
func (m *mac) print() string {
return m.printer.printFile()
}
func (m *mac) setPrinter(p printer) {
m.printer = p
}
type printer interface {
printFile() string
}
type epson struct {}
func (e *epson) printFile() string {
return "epson"
}
type samsung struct {}
func (e *samsung) printFile() string {
return "samsung"
}
test
package bridge
import "testing"
func TestFactory(t *testing.T) {
mac := &mac{}
epson := &epson{}
mac.setPrinter(epson)
if mac.print() != "epson" {
t.Error("epson 프린터 오류")
}
samsung := &samsung{}
mac.setPrinter(samsung)
if mac.print() != "samsung" {
t.Error("samsung 프린터 오류")
}
}
Composite Pattern
package composite
import "fmt"
type fileFolderComponent interface {
display()
}
type file struct {
name string
}
func (f *file) display() {
fmt.Println(f.name)
}
type folder struct {
components []fileFolderComponent
name string
}
func (f *folder) display() {
fmt.Println(f.name)
for _, composite := range f.components {
composite.display()
}
}
func (f *folder) add(c fileFolderComponent) {
f.components = append(f.components, c)
}
test
package composite
import "testing"
func TestFactory(t *testing.T) {
file1 := &file{name: "file1"}
file2 := &file{name: "file2"}
file3 := &file{name: "file3"}
folder1 := &folder{
name: "/",
}
folder1.add(file1)
folder2 := &folder{
name: "/sub",
}
folder2.add(file2)
folder2.add(file3)
folder1.add(folder2)
folder1.display()
}
Flyweight Pattern
package flyweight
import "fmt"
type enermy struct {
name string
cached bool
}
type iEnermy interface {
getName() string
attack()
setCached(cache bool)
getCached() bool
}
func (e *enermy) getName() string{
return e.name
}
func (e *enermy) attack() {
fmt.Print("attack")
}
func (e *enermy) setCached(cache bool) {
e.cached = cache
}
func (e *enermy) getCached() bool{
return e.cached
}
type enermyFactory struct {
enermies map[string]iEnermy
}
func (ef *enermyFactory) getEnermy(ename string) (iEnermy, error) {
if ef.enermies[ename] != nil {
ef.enermies[ename].setCached(true)
return ef.enermies[ename], nil
}
ef.enermies[ename] = &enermy{
name: ename,
cached: false,
}
return ef.enermies[ename], nil
}
test
package flyweight
import "testing"
func TestFactory(t *testing.T) {
enermyFactory := &enermyFactory{
enermies: map[string]iEnermy{},
}
enermy1, _ := enermyFactory.getEnermy("goblin")
if enermy1.getName() != "goblin" {
t.Error("goblin이 아님")
}
enermy2 , _ := enermyFactory.getEnermy("devil")
if enermy2.getName() != "devil" {
t.Error("devil 아님")
}
enermy3, _ := enermyFactory.getEnermy("goblin")
if enermy3.getCached() != true {
t.Error("goblin이 캐쉬되지 않음")
}
}
Facade Pattern
package facade
type facade struct {
toResult iVeryComplexLogic
}
type iVeryComplexLogic interface {
idontknow() string
}
func (f *facade) idontknow() string {
return "idontknow"
}
test
import "testing"
func TestFactory(t *testing.T) {
facade := facade{}
if facade.idontknow() != "idontknow" {
t.Error("idontknow 아님")
}
}
Proxy Pattern
package proxy
type iProxyService interface {
connectSite() string
}
type proxyService struct {
}
func (p *proxyService) connectSite() string {
return "connected"
}
type proxyPattern struct {
proxy iProxyService
}
func (p *proxyPattern) run() string {
return p.proxy.connectSite()
}
test
package proxy
import (
"testing"
)
func TestFactory(t *testing.T) {
proxyPattern := proxyPattern{
proxy: &proxyService{},
}
if proxyPattern.run() != "connected" {
t.Error("proxy 연결안됨")
}
}
Behavior Patterns
Strategy Pattern
package behavior
type iDiscount interface {
discount() int
}
type ruleChild struct {
}
func (c *ruleChild) discount() int {
return -1000
}
type ruleAdult struct {
}
func (c *ruleAdult) discount() int {
return 0
}
type busChager struct {
basePrice int
}
func (b *busChager) getPrice(d iDiscount) int {
return b.basePrice - d.discount()
}
test
package behavior
import "testing"
func TestFactory(t *testing.T) {
busChager := busChager{
basePrice: 1500,
}
child := &ruleChild{}
if busChager.getPrice(child) != 500 {
t.Error("아동 할인 오류")
}
adult := &ruleAdult{}
if busChager.getPrice(adult) != 1500 {
t.Error("어른은 얄짤 없음")
}
}
Strategy Pattern
package state
type states interface {
getStateTest() string
nextSteate() states
}
type born struct {}
func (b *born) getStateTest() string {
return "태어남"
}
func (b *born) nextSteate() states {
return &infant{}
}
type infant struct {}
func (b *infant) getStateTest() string {
return "아기때"
}
func (b *infant) nextSteate() states {
return &adult{}
}
type adult struct {}
func (b *adult) getStateTest() string {
return "어른"
}
func (b *adult) nextSteate() states {
return &die{}
}
type die struct {}
func (b *die) getStateTest() string {
return "죽어서 환생준비"
}
func (b *die) nextSteate() states {
return &born{}
}
type lifeIs struct {
state states
}
func (life *lifeIs) nextStep() {
life.state = life.state.nextSteate()
}
test
import (
"testing"
)
func TestFactory(t *testing.T) {
mylife := &lifeIs{ state: &born{}}
if mylife.state.getStateTest() != "태어남" {
t.Error("아직 안태어남")
}
mylife.nextStep()
mylife.nextStep()
if mylife.state.getStateTest() != "어른" {
t.Error("아직 어른이?")
}
}
Command Pattern
package command
type parts interface {
on()
off()
}
type printer struct {
status string
}
func (p *printer) on() {
p.status = "on"
}
func (p *printer) off() {
p.status = "off"
}
type commander interface {
execute(p parts)
}
type turnonCommand struct {
}
func (t *turnonCommand) execute(p parts) {
p.on()
}
type remoteControler struct {
commander
}
func (r *remoteControler) powerOn(p parts) {
r.commander.execute(p)
}
test
package command
import "testing"
func TestFactory(t *testing.T) {
remoteController := &remoteControler{
commander: &turnonCommand{},
}
printer := &printer{ status: "off"}
remoteController.powerOn(printer)
if printer.status != "on" {
t.Error("on")
}
}
Chain Of Responsibility Pattern
package chainofresponsibility
type process interface {
canBill(amount int) bool
runBill(amount int)
next() bankinfo
}
type bankinfo interface {
process
setNextBank(nb bankinfo)
}
type bank1 struct {
remain int
nextBank bankinfo
}
func (b *bank1) next() bankinfo {
return b.nextBank
}
func (b *bank1) setNextBank(nb bankinfo) {
b.nextBank = nb
}
func (b *bank1) canBill(amount int) bool {
return b.remain - amount > 0
}
func (b *bank1) runBill(amount int) {
b.remain = b.remain - amount
}
type bank2 struct {
remain int
nextBank bankinfo
}
func (b *bank2) next() bankinfo {
return b.nextBank
}
func (b *bank2) setNextBank(nb bankinfo) {
b.nextBank = nb
}
func (b *bank2) canBill(amount int) bool {
return b.remain - amount > 0
}
func (b *bank2) runBill(amount int) {
b.remain = b.remain - amount
}
type bank3 struct {
remain int
nextBank bankinfo
}
func (b *bank3) next() bankinfo {
return b.nextBank
}
func (b *bank3) setNextBank(nb bankinfo) {
b.nextBank = nb
}
func (b *bank3) canBill(amount int) bool {
return b.remain - amount > 0
}
func (b *bank3) runBill(amount int) {
b.remain = b.remain - amount
}
type chainOfBank struct {
bankinfo
}
func (c *chainOfBank) next() {
c.bankinfo = c.bankinfo.next()
}
test
package chainofresponsibility
import "testing"
func TestFactory(t *testing.T) {
bank3 := &bank3{
remain: 1000,
nextBank: nil,
}
bank2 := &bank2{
remain: 5000,
nextBank: bank3,
}
bank1 := &bank1{
remain: 1000,
nextBank: bank2,
}
wantMoney := 3000
remoteController := &chainOfBank{
bankinfo: bank1,
}
if remoteController.bankinfo.canBill(wantMoney) {
t.Error("에헤헤..은행1 미쳤냐?")
}
remoteController.next()
if !remoteController.bankinfo.canBill(wantMoney) {
t.Error("에헤헤..은행2 미쳤냐?")
}
}
Mediator Pattern
https://golangbyexample.com/mediator-design-pattern-golang/ 참고하였음. kotlin의 비행관제센터와 같은 맥락임.
package Mediator
import "fmt"
type train interface {
arrive()
depart()
permitArrival()
}
type passengerTrain struct {
mediator mediator
}
func (g *passengerTrain) arrive() {
if !g.mediator.canArrive(g) {
fmt.Println("PassengerTrain: Arrival blocked, waiting")
return
}
fmt.Println("PassengerTrain: Arrived")
}
func (g *passengerTrain) depart() {
fmt.Println("PassengerTrain: Leaving")
g.mediator.notifyAboutDeparture()
}
func (g *passengerTrain) permitArrival() {
fmt.Println("PassengerTrain: Arrival permitted, arriving")
g.arrive()
}
type freightTrain struct {
mediator mediator
}
func (g *freightTrain) arrive() {
if !g.mediator.canArrive(g) {
fmt.Println("FreightTrain: Arrival blocked, waiting")
return
}
fmt.Println("FreightTrain: Arrived")
}
func (g *freightTrain) depart() {
fmt.Println("FreightTrain: Leaving")
g.mediator.notifyAboutDeparture()
}
func (g *freightTrain) permitArrival() {
fmt.Println("FreightTrain: Arrival permitted")
g.arrive()
}
type mediator interface {
canArrive(train) bool
notifyAboutDeparture()
}
type stationManager struct {
isPlatformFree bool
trainQueue []train
}
func newStationManger() *stationManager {
return &stationManager{
isPlatformFree: true,
}
}
func (s *stationManager) canArrive(t train) bool {
if s.isPlatformFree {
s.isPlatformFree = false
return true
}
s.trainQueue = append(s.trainQueue, t)
return false
}
func (s *stationManager) notifyAboutDeparture() {
if !s.isPlatformFree {
s.isPlatformFree = true
}
if len(s.trainQueue) > 0 {
firstTrainInQueue := s.trainQueue[0]
s.trainQueue = s.trainQueue[1:]
firstTrainInQueue.permitArrival()
}
}
test
package Mediator
import (
"testing"
)
func TestFactory(t *testing.T) {
stationManager := newStationManger()
passengerTrain := &passengerTrain{
mediator: stationManager,
}
freightTrain := &freightTrain{
mediator: stationManager,
}
passengerTrain.arrive()
freightTrain.arrive()
passengerTrain.depart()
}
Mediator Pattern
package memento
// 기념비에 대한 설명을 보존
type originator struct {
state string
}
func (e *originator) createMemento() *memento {
return &memento{state: e.state}
}
func (e *originator) restoreMemento(m *memento) {
e.state = m.getSavedState()
}
func (e *originator) setState(state string) {
e.state = state
}
func (e *originator) getState() string {
return e.state
}
//순수객체
type memento struct {
state string
}
func (m *memento) getSavedState() string {
return m.state
}
// 기념비목록을 추가,관리
type caretaker struct {
mementoArray []*memento
}
func (c *caretaker) addMemento(m *memento) {
c.mementoArray = append(c.mementoArray, m)
}
func (c *caretaker) getMemento(index int) *memento {
return c.mementoArray[index]
}
test
type originator struct {
state string
}
func (e *originator) createMemento() *memento {
return &memento{state: e.state}
}
func (e *originator) restoreMemento(m *memento) {
e.state = m.getSavedState()
}
func (e *originator) setState(state string) {
e.state = state
}
func (e *originator) getState() string {
return e.state
}
//순수객체
type memento struct {
state string
}
func (m *memento) getSavedState() string {
return m.state
}
// 기념비목록을 추가,관리
type caretaker struct {
mementoArray []*memento
}
func (c *caretaker) addMemento(m *memento) {
c.mementoArray = append(c.mementoArray, m)
}
func (c *caretaker) getMemento(index int) *memento {
return c.mementoArray[index]
}
Visitor Pattern
package visitor
import "fmt"
type camera interface {
display()
cleanCheck()
visit() bool
}
type Room1 struct {
visior iVisitor
}
func (r *Room1) display() {
fmt.Print("1번방 카메라보기")
}
func (r *Room1) cleanCheck() {
fmt.Print("1번방 청소확인")
}
func (r *Room1) visit() bool{
r.visior.visitForRoom1(r)
return true
}
type Room2 struct {
visior iVisitor
}
func (r *Room2) display() {
fmt.Println("1번방 카메라보기")
}
func (r *Room2) cleanCheck() {
fmt.Println("1번방 청소확인")
}
func (r *Room2) visit() bool{
r.visior.visitForRoom2(r)
return true;
}
type iVisitor interface {
visitForRoom1(room1 *Room1)
visitForRoom2(room2 *Room2)
}
type visitor struct {
visitorName string
}
func (v *visitor) visitForRoom1(room1 *Room1) {
fmt.Println(v.visitorName)
room1.display()
room1.cleanCheck()
}
func (v *visitor) visitForRoom2(room2 *Room2) {
fmt.Println(v.visitorName)
room2.display()
room2.cleanCheck()
}
test
package visitor
import (
"testing"
)
func TestFactory(t *testing.T) {
visitor1 := visitor{visitorName: "책임운영자"}
room1 := Room1{
visior: &visitor1,
}
if !room1.visit() {
t.Error("책임운영자 1번방 확인실패")
}
visitor2 := visitor{visitorName: "감시자"}
room1 = Room1{
visior: &visitor2,
}
if !room1.visit() {
t.Error("감시자 1번방 확인실패")
}
}
Template Pattern
package template
import "fmt"
type dailyRoutine interface {
commute()
toWork
eatLunch()
goHome()
showTeamsDailyroutine()
}
type toWork interface {
toWork()
}
type commonRoutine struct {
toWork
}
func (c *commonRoutine) commute() {
fmt.Println("commute")
}
func (c *commonRoutine) eatLunch() {
fmt.Println("eatLunch")
}
func (c *commonRoutine) goHome() {
fmt.Println("gohome")
}
func (c *commonRoutine) showTeamsDailyroutine() {
c.commute()
c.toWork.toWork()
c.eatLunch()
c.goHome()
}
type hrTeam struct {
}
func (h *hrTeam) toWork() {
fmt.Println("work is seek!seek!seek!")
}
type devTeam struct {
}
func (h *devTeam) toWork() {
fmt.Println("work is coding!coding!coding!")
}
test
package template
import "testing"
func TestFactory(t *testing.T) {
hrTeamWork := hrTeam{}
baseRoutine := commonRoutine{
toWork: &hrTeamWork,
}
baseRoutine.showTeamsDailyroutine()
baseRoutine = commonRoutine{
toWork: &devTeam{},
}
baseRoutine.showTeamsDailyroutine()
}
Observer Pattern
package Observer
import (
"container/list"
"fmt")
type subscriber struct {
name string
}
func (s *subscriber) onUpdate(msg string) {
fmt.Println("구독자:", s.name, "메시지:", msg)
}
type iPublisher interface {
update(text string)
add(subscriber)
delete(subscriber)
}
type newsPublisher struct {
subscribers *list.List
}
func (n *newsPublisher) update(text string) {
for e := n.subscribers.Front(); e != nil; e = e.Next() {
s := e.Value.(subscriber)
s.onUpdate(text)
}
}
func (n *newsPublisher) add(s subscriber) {
n.subscribers.PushBack(s)
}
func (n *newsPublisher) delete(s subscriber) {
var removeItem subscriber
for e := n.subscribers.Front(); e != nil; e = e.Next() {
removeItem = e.Value.(subscriber)
if s == removeItem {
n.subscribers.Remove(e)
break;
}
}
}
test
package Observer
import (
"container/list"
"testing")
func TestFactory(t *testing.T) {
newsCenter := newsPublisher{
subscribers: new(list.List),
}
subscriber1 := subscriber{name: "sub1"}
subscriber2 := subscriber{name: "sub2"}
subscriber3 := subscriber{name: "sub3"}
newsCenter.add(subscriber1)
newsCenter.add(subscriber2)
newsCenter.add(subscriber3)
newsCenter.update("new~~~s")
newsCenter.delete(subscriber2)
newsCenter.update("1,3 new~~~s")
}
0 comments:
댓글 쓰기