packagefactorymethodtypeSenderinterface{Send(string)string}typeCreatorinterface{NewSender()Sender}// Deliver depends on a creation operation, not a concrete product.funcDeliver(cCreator,messagestring)string{returnc.NewSender().Send(message)}typeEmailCreatorstruct{}func(EmailCreator)NewSender()Sender{returnemail{}}typeSMSCreatorstruct{}func(SMSCreator)NewSender()Sender{returnsms{}}typeemailstruct{}func(email)Send(sstring)string{return"email:"+s}typesmsstruct{}func(sms)Send(sstring)string{return"sms:"+s}
// Builder creates a balancer.typeBuilderinterface{// Build creates a new balancer with the ClientConn.Build(ccClientConn,optsBuildOptions)Balancer// Name returns the name of balancers built by this builder.// It will be used to pick balancers (for example in service config).Name()string}
packageabstractfactorytypeButtoninterface{Draw()string}typeCheckboxinterface{Check()string}typeFactoryinterface{Button()ButtonCheckbox()Checkbox}typebuttonstringfunc(bbutton)Draw()string{returnstring(b)+":button"}typecheckboxstringfunc(ccheckbox)Check()string{returnstring(c)+":checked"}typeLightstruct{}func(Light)Button()Button{returnbutton("light")}func(Light)Checkbox()Checkbox{returncheckbox("light")}typeDarkstruct{}func(Dark)Button()Button{returnbutton("dark")}func(Dark)Checkbox()Checkbox{returncheckbox("dark")}// Screen obtains related products from the same family factory.funcScreen(fFactory)string{returnf.Button().Draw()+"/"+f.Checkbox().Check()}
// Conn is a connection to a database. It is not used concurrently// by multiple goroutines.//// Conn is assumed to be stateful.typeConninterface{// Prepare returns a prepared statement, bound to this connection.Prepare(querystring)(Stmt,error)// Close invalidates and potentially stops any current// prepared statements and transactions, marking this// connection as no longer in use.//// Because the sql package maintains a free pool of// connections and only calls Close when there's a surplus of// idle connections, it shouldn't be necessary for drivers to
func(mc*mysqlConn)Prepare(querystring)(driver.Stmt,error){ifmc.closed.Load(){returnnil,driver.ErrBadConn}// Send commanderr:=mc.writeCommandPacketStr(comStmtPrepare,query)iferr!=nil{// STMT_PREPARE is safe to retry. So we can return ErrBadConn here.mc.log(err)returnnil,driver.ErrBadConn}stmt:=&mysqlStmt{mc:mc,}
packagebuilderimport"errors"typePlanstruct{NamestringSteps[]string}typeBuilderstruct{namestringsteps[]string}func(b*Builder)Named(sstring)*Builder{b.name=s;returnb}func(b*Builder)Add(sstring)*Builder{b.steps=append(b.steps,s);returnb}func(b*Builder)Build()(Plan,error){ifb.name==""||len(b.steps)==0{returnPlan{},errors.New("name and steps required")}returnPlan{Name:b.name,Steps:append([]string(nil),b.steps...)},nil}
packagebuilderimport"testing"funcTestValidationAndBuiltValueOwnership(t*testing.T){b:=new(Builder)if_,err:=b.Build();err==nil{t.Fatal("invalid plan accepted")}first,err:=b.Named("import").Add("read").Build()iferr!=nil{t.Fatal(err)}first.Steps[0]="changed by caller"second,err:=b.Add("write").Build()iferr!=nil{t.Fatal(err)}ifsecond.Steps[0]!="read"||len(second.Steps)!=2||len(first.Steps)!=1{t.Fatal(first,second)}}
// Verb sets the verb this request will use.func(r*Request)Verb(verbstring)*Request{r.verb=verbreturnr}// Prefix adds segments to the relative beginning to the request path. These// items will be placed before the optional Namespace, Resource, or Name sections.// Setting AbsPath will clear any previously set Prefix segmentsfunc(r*Request)Prefix(segments...string)*Request{ifr.err!=nil{returnr}r.pathPrefix=path.Join(r.pathPrefix,path.Join(segments...))returnr
func(r*Request)Name(resourceNamestring)*Request{ifr.err!=nil{returnr}iflen(resourceName)==0{r.err=fmt.Errorf("resource name may not be empty")returnr}iflen(r.resourceName)!=0{r.err=fmt.Errorf("resource name already set to %q, cannot change to %q",r.resourceName,resourceName)returnr}ifmsgs:=IsValidPathSegmentName(resourceName);len(msgs)!=0{r.err=fmt.Errorf("invalid resource name %q: %v",resourceName,msgs)returnr
// Error type:// - If the server responds with a status: *errors.StatusError or *errors.UnexpectedObjectError// - http.Client.Do errors are returned directly.func(r*Request)Do(ctxcontext.Context)Result{logger:=klog.FromContext(ctx)ifr.body==nil{logBody(logger,2,"Request Body",r.bodyBytes)}varresultResulterr:=r.request(ctx,func(req*http.Request,resp*http.Response){result=r.transformResponse(ctx,resp,req)})iferr!=nil{returnResult{err:err,logger:logger}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.func(in*Pod)DeepCopyInto(out*Pod){*out=*inout.TypeMeta=in.TypeMetain.ObjectMeta.DeepCopyInto(&out.ObjectMeta)in.Spec.DeepCopyInto(&out.Spec)in.Status.DeepCopyInto(&out.Status)return}// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pod.func(in*Pod)DeepCopy()*Pod{ifin==nil{returnnil}
packagesingletonimport"sync"// Settings exposes no mutation API; all callers share one package default.typeSettingsstruct{endpointstring}func(s*Settings)Endpoint()string{returns.endpoint}varinstance=sync.OnceValue(func()*Settings{return&Settings{endpoint:"localhost:8080"}})funcDefault()*Settings{returninstance()}
// Local represents the system's local time zone.// On Unix systems, Local consults the TZ environment// variable to find the time zone to use. No TZ means// use the system default /etc/localtime.// TZ="" means use UTC.// TZ="foo" means use file foo in the system timezone directory.varLocal*Location=&localLoc// localLoc is separate so that initLocal can initialize// it even if a client has changed Local.varlocalLocLocationvarlocalOncesync.Oncefunc(l*Location)get()*Location{ifl==nil{
// returned by f. The returned function may be called concurrently.//// If f panics, the returned function will panic with the same value on every call.funcOnceValue[Tany](ffunc()T)func()T{// Use a struct so that there's a single heap allocation.d:=struct{ffunc()TonceOncevalidboolpanyresultT}{f:f,}returnfunc()T{
// ordinary functions as HTTP handlers. If f is a function// with the appropriate signature, HandlerFunc(f) is a// [Handler] that calls f.typeHandlerFuncfunc(ResponseWriter,*Request)// ServeHTTP calls f(w, r).func(fHandlerFunc)ServeHTTP(wResponseWriter,r*Request){f(w,r)}
typeLoggerstruct{corezapcore.CoredevelopmentbooladdCallerboolonPaniczapcore.CheckWriteHook// default is WriteThenPaniconFatalzapcore.CheckWriteHook// default is WriteThenFatalnamestringerrorOutputzapcore.WriteSynceraddStackzapcore.LevelEnablercallerSkipint
// Core is a minimal, fast logger interface. It's designed for library authors// to wrap in a more user-friendly API.typeCoreinterface{LevelEnabler// With adds structured context to the Core.With([]Field)Core// Check determines whether the supplied Entry should be logged (using the// embedded LevelEnabler and possibly some extra logic). If the entry// should be logged, the Core adds itself to the CheckedEntry and returns// the result.//// Callers must use Check before calling Write.Check(Entry,*CheckedEntry)*CheckedEntry// Write serializes the Entry and any Fields supplied at the log site and
func(log*Logger)check(lvlzapcore.Level,msgstring)*zapcore.CheckedEntry{// Logger.check must always be called directly by a method in the// Logger interface (e.g., Check, Info, Fatal).// This skips Logger.check and the Info/Fatal/Check/etc. method that// called it.constcallerSkipOffset=2// Check the level first to reduce the cost of disabled log calls.// Since Panic and higher may exit, we skip the optimization for those levels.iflvl<zapcore.DPanicLevel&&!log.core.Enabled(lvl){returnnil}// Create basic checked entry thru the core; this will be non-nil if the// log message will actually be written somewhere.
test 包含 leaf、两层 nested directory 和空 directory,验证统一 contract 与递归 aggregation。
完整 file,含 package 与 imports。和 ZIP 中的代码逐字一致。
pattern.go · 17 lines
packagecomposite// Node represents a finite, acyclic tree. Cycles are outside this contract.typeNodeinterface{Size()int}typeFileintfunc(fFile)Size()int{returnint(f)}typeDirectory[]Nodefunc(dDirectory)Size()int{total:=0for_,child:=ranged{total+=child.Size()}returntotal}
go test -v ./composite在解压后的 gof-min 目录执行
验证 behavior,而不只是能 compilation
test 包含 leaf、两层 nested directory 和空 directory,验证统一 contract 与递归 aggregation。
// Join returns an error that wraps the given errors.// Any nil error values are discarded.// Join returns nil if every value in errs is nil.// The error formats as the concatenation of the strings obtained// by calling the Error method of each element of errs, with a newline// between each string.//// A non-nil error returned by Join implements the Unwrap() []error method.// The errors may be inspected with [Is] and [As].funcJoin(errs...error)error{n:=0for_,err:=rangeerrs{iferr!=nil{n++}
packagedecoratorimport("io""strings""testing")typefinalChunkstruct{}func(finalChunk)Read(p[]byte)(int,error){returncopy(p,"end"),io.EOF}funcTestPreservesBytesAndTerminalError(t*testing.T){reader:=&CountingReader{Source:strings.NewReader("hello")}got,err:=io.ReadAll(reader)iferr!=nil||string(got)!="hello"||reader.Bytes!=5{t.Fatal(string(got),err,reader.Bytes)}reader=&CountingReader{Source:finalChunk{}}n,err:=reader.Read(make([]byte,3))ifn!=3||err!=io.EOF||reader.Bytes!=3{t.Fatal("lost bytes returned with EOF")}}
// LimitReader returns a Reader that reads from r// but stops with EOF after n bytes.// The underlying implementation is a *LimitedReader.funcLimitReader(rReader,nint64)Reader{return&LimitedReader{r,n}}// A LimitedReader reads from R but limits the amount of// data returned to just N bytes. Each call to Read// updates N to reflect the new amount remaining.// Read returns EOF when N <= 0 or when the underlying R returns EOF.typeLimitedReaderstruct{RReader// underlying readerNint64// max bytes remaining}func(l*LimitedReader)Read(p[]byte)(nint,errerror){
packagefacadeimport("errors""testing")typestoreFuncfunc(string)(string,error)func(fstoreFunc)Get(kstring)(string,error){returnf(k)}typedecodeFuncfunc(string)(Config,error)func(fdecodeFunc)Decode(sstring)(Config,error){returnf(s)}funcTestFacadeOrchestratesAndPreservesError(t*testing.T){missing:=errors.New("missing")decoded:=falses:=Service{Store:storeFunc(func(string)(string,error){return"",missing}),Decoder:decodeFunc(func(rawstring)(Config,error){decoded=true;returnConfig{Address:raw},nil})}if_,err:=s.Load("key");!errors.Is(err,missing)||decoded{t.Fatal("decode ran after failed load",err)}s.Store=storeFunc(func(string)(string,error){return"localhost",nil})ifgot,err:=s.Load("key");err!=nil||got.Address!="localhost"{t.Fatal(got,err)}}
funcGet(urlstring)(resp*Response,errerror){returnDefaultClient.Get(url)}// Get issues a GET to the specified URL. If the response is one of the// following redirect codes, Get follows the redirect after calling the// [Client.CheckRedirect] function://// 301 (Moved Permanently)// 302 (Found)// 303 (See Other)// 307 (Temporary Redirect)// 308 (Permanent Redirect)//// An error is returned if the [Client.CheckRedirect] function fails
packageflyweightimport"sync"typeStylestruct{fontstring}func(s*Style)Font()string{returns.font}// Pool keeps strong references forever; a real unbounded key space needs a policy.typePoolstruct{musync.Mutexstylesmap[string]*Style}func(p*Pool)Get(fontstring)*Style{p.mu.Lock()deferp.mu.Unlock()ifp.styles==nil{p.styles=make(map[string]*Style)}ifp.styles[font]==nil{p.styles[font]=&Style{font:font}}returnp.styles[font]}// Extrinsic data stays outside the shared immutable Style.typeGlyphstruct{Style*StyleTextstringXint}
// Handle is a globally unique identity for some value of type T.//// Two handles compare equal exactly if the two values used to create the handles// would have also compared equal. The comparison of two handles is trivial and// typically much more efficient than comparing the values used to create them.typeHandle[Tcomparable]struct{value*T}// Value returns a shallow copy of the T value that produced the Handle.// Value is safe for concurrent use by multiple goroutines.func(hHandle[T])Value()T{return*h.value}
func(p*ReverseProxy)ServeHTTP(rwhttp.ResponseWriter,req*http.Request){transport:=p.Transportiftransport==nil{transport=http.DefaultTransport}ctx:=req.Context()ifctx.Done()!=nil{// CloseNotifier predates context.Context, and has been// entirely superseded by it. If the request contains// a Context that carries a cancellation signal, don't// bother spinning up a goroutine to watch the CloseNotify// channel (if any).//// If the request Context has a nil Done channel (which
// Next should be used only inside middleware.// It executes the pending handlers in the chain inside the calling handler.// See example in GitHub.func(c*Context)Next(){c.index++forc.index<safeInt8(len(c.handlers)){ifc.handlers[c.index]!=nil{c.handlers[c.index](c)}c.index++}}// IsAborted returns true if the current context was aborted.func(c*Context)IsAborted()bool{
rule=admin AND NOT blocked。test 同时覆盖 true、missing variable 和 false AND missing 的 short-circuit。
go test -v ./interpreter在解压后的 gof-min 目录执行
pattern_test.go
packageinterpreterimport"testing"funcTestExpressionSemanticsAndShortCircuit(t*testing.T){rule:=And{Left:Var("admin"),Right:Not{Inner:Var("blocked")}}ifgot,err:=rule.Eval(Env{"admin":true,"blocked":false});err!=nil||!got{t.Fatal(got,err)}ifgot,err:=(And{Left:Literal(false),Right:Var("missing")}).Eval(nil);err!=nil||got{t.Fatal("not short circuited")}if_,err:=Var("missing").Eval(nil);err==nil{t.Fatal("unknown silently accepted")}}
Go adaptationtext/template 的 parse.Node 表达 syntax 结构,exec.state.walk 按 node kind 解释 action/list/if/text。它用 central type switch,而不是每个 node 自带 Interpret method;这是 Go-style tree interpretation,不是经典 class hierarchy 的逐字翻译。
// generating output as they go.func(s*state)walk(dotreflect.Value,nodeparse.Node){s.at(node)switchnode:=node.(type){case*parse.ActionNode:// Do not pop variables so they persist until next end.// Also, if the action declares variables, don't print the result.val:=s.evalPipeline(dot,node.Pipe)iflen(node.Pipe.Decl)==0{s.printValue(node,val)}case*parse.BreakNode:panic(walkBreak)case*parse.CommentNode:case*parse.ContinueNode:
// A Node is an element in the parse tree. The interface is trivial.// The interface contains an unexported method so that only// types local to this package can satisfy it.typeNodeinterface{Type()NodeTypeString()string// Copy does a deep copy of the Node and all its components.// To avoid type assertions, some XxxNodes also have specialized// CopyXxx methods that return *XxxNode.Copy()NodePosition()Pos// byte position of start of node in full original input string// tree returns the containing *Tree.// It is unexported so all implementations of Node are in this package.tree()*Tree// writeTo writes the String output to the builder.
// AscendRange calls the iterator for every value in the tree within the range// [greaterOrEqual, lessThan), until iterator returns false.func(t*BTree)AscendRange(greaterOrEqual,lessThanItem,iteratorItemIterator){ift.root==nil{return}t.root.iterate(ascend,greaterOrEqual,lessThan,true,false,iterator)}// AscendLessThan calls the iterator for every value in the tree within the range// [first, pivot), until iterator returns false.func(t*BTree)AscendLessThan(pivotItem,iteratorItemIterator){ift.root==nil{return}
// Preorder returns an iterator over all the nodes of the syntax tree// beneath (and including) the specified root, in depth-first// preorder.//// For greater control over the traversal of each subtree, use// [Inspect] or [PreorderStack].funcPreorder(rootNode)iter.Seq[Node]{returnfunc(yieldfunc(Node)bool){ok:=trueInspect(root,func(nNode)bool{ifn!=nil{// yield must not be called once ok is false.ok=ok&&yield(n)}returnok
a 不持有 b 的 pointer,仍能通过 mediator 给 b 发消息。test 验证 unknown recipient、duplicate identity 与 inbox ownership。
完整 file,含 package 与 imports。和 ZIP 中的代码逐字一致。
pattern.go · 36 lines
packagemediatorimport("fmt""slices")// Room and Members have a single owner; concurrent use needs a separate policy.typeRoomstruct{membersmap[string]*Member}typeMemberstruct{room*Roomnamestringinbox[]string}func(r*Room)Join(namestring)(*Member,error){ifr.members==nil{r.members=make(map[string]*Member)}if_,exists:=r.members[name];exists{returnnil,fmt.Errorf("duplicate member %s",name)}m:=&Member{room:r,name:name}r.members[name]=mreturnm,nil}func(m*Member)Send(to,textstring)error{returnm.room.route(m.name,to,text)}func(m*Member)Messages()[]string{returnslices.Clone(m.inbox)}func(r*Room)route(from,to,textstring)error{recipient,ok:=r.members[to]if!ok{returnfmt.Errorf("unknown recipient %s",to)}recipient.inbox=append(recipient.inbox,from+":"+text)returnnil}
go test -v ./mediator在解压后的 gof-min 目录执行
验证 behavior,而不只是能 compilation
a 不持有 b 的 pointer,仍能通过 mediator 给 b 发消息。test 验证 unknown recipient、duplicate identity 与 inbox ownership。
// Events is a type that associates EventType to any datatypeEventsmap[EventType]any// EventBox is used for coordinating eventstypeEventBoxstruct{eventsEventscond*sync.Condignoremap[EventType]bool}// NewEventBox returns a new EventBoxfuncNewEventBox()*EventBox{return&EventBox{events:make(Events),cond:sync.NewCond(&sync.Mutex{}),
editor 需要 undo,但 history manager 不应该知道或修改 editor 内部 field。
最小 implementation 的设计
Save 返回 opaque Snapshot, field 不 exported;Restore 由 Editor 自己执行,并拒绝其它 Editor 的 snapshot。caretaker 只保存 token,不解释内容。
OriginatorEditor
MementoSnapshot
Caretakercaller / history
顺着 code 读
test 保存 before、改成 after、再恢复,并检查 snapshot 可重复使用及跨 owner restore 被拒绝。
完整 file,含 package 与 imports。和 ZIP 中的代码逐字一致。
pattern.go · 20 lines
packagemementoimport"errors"typeEditorstruct{textstring}typeSnapshotstruct{owner*Editortextstring}func(e*Editor)Set(sstring){e.text=s}func(e*Editor)Text()string{returne.text}func(e*Editor)Save()Snapshot{returnSnapshot{owner:e,text:e.text}}func(e*Editor)Restore(sSnapshot)error{ifs.owner!=e{returnerrors.New("snapshot belongs to another editor")}e.text=s.textreturnnil}
go test -v ./memento在解压后的 gof-min 目录执行
验证 behavior,而不只是能 compilation
test 保存 before、改成 after、再恢复,并检查 snapshot 可重复使用及跨 owner restore 被拒绝。
// Snapshot returns an identifier for the current revision of the state.func(s*StateDB)Snapshot()int{returns.journal.snapshot()}// RevertToSnapshot reverts all state changes made since the given revision.func(s*StateDB)RevertToSnapshot(revidint){s.journal.revertToSnapshot(revid,s)}
// snapshot returns an identifier for the current revision of the state.func(j*journal)snapshot()int{id:=j.nextRevisionIdj.nextRevisionId++j.validRevisions=append(j.validRevisions,revision{id,j.length()})returnid}// revertToSnapshot reverts all state changes made since the given revision.func(j*journal)revertToSnapshot(revidint,s*StateDB){// Find the snapshot in the stack of valid snapshots.idx:=sort.Search(len(j.validRevisions),func(iint)bool{returnj.validRevisions[i].id>=revid})ifidx==len(j.validRevisions)||j.validRevisions[idx].id!=revid{
self-unsubscribe test 连续 Publish 两次,只收到第一次。parallel Publish test 用 atomic counter,因为 callback 本身可能 concurrent 执行。
完整 file,含 package 与 imports。和 ZIP 中的代码逐字一致。
pattern.go · 35 lines
packageobserverimport"sync"typeTopicstruct{musync.Mutexnextintlistenersmap[int]func(string)}func(t*Topic)Subscribe(fnfunc(string))func(){t.mu.Lock()defert.mu.Unlock()ift.listeners==nil{t.listeners=make(map[int]func(string))}id:=t.nextt.next++t.listeners[id]=fnreturnfunc(){t.mu.Lock();defert.mu.Unlock();delete(t.listeners,id)}}// Delivery is synchronous, unordered, and uses a listener snapshot.// Unsubscribe cannot revoke a callback already included in a snapshot.func(t*Topic)Publish(valuestring){t.mu.Lock()snapshot:=make([]func(string),0,len(t.listeners))for_,fn:=ranget.listeners{snapshot=append(snapshot,fn)}t.mu.Unlock()for_,fn:=rangesnapshot{fn(value)}}
go test -v ./observer在解压后的 gof-min 目录执行
验证 behavior,而不只是能 compilation
self-unsubscribe test 连续 Publish 两次,只收到第一次。parallel Publish test 用 atomic counter,因为 callback 本身可能 concurrent 执行。
func(p*sharedProcessor)addListener(listener*processorListener)(ResourceEventHandlerRegistration,bool){p.listenersLock.Lock()deferp.listenersLock.Unlock()ifp.listeners==nil{p.listeners=make(map[*processorListener]bool)}p.listeners[listener]=trueifp.listenersStarted{// Not starting listener.watchSynced!// The caller must first add the initial list, then start it.p.wg.Start(listener.run)p.wg.Start(listener.pop)
func(p*sharedProcessor)distribute(objinterface{},syncbool){p.listenersLock.RLock()deferp.listenersLock.RUnlock()// Before we start blocking on writes to the listeners' channels,// ensure that they all have been started. If the processor stops,// p.listeners gets cleared, in which case we also continue here// and return without doing anything.for!p.listenersStarted&&len(p.listeners)>0{p.listenersRCond.Wait()}forlistener,isSyncing:=rangep.listeners{switch{case!sync:
Gate 把 Coin/Pass delegate 给 current mode。具体 state 决定 result 和 next state;locked.Coin 进入 unlocked,unlocked.Pass 再进入 locked。client 不直接选择 algorithm。
ContextGate
State interfacemode
Concrete stateslocked / unlocked
顺着 code 读
test 依次检查拒绝、投入 coin、允许、再次拒绝。第二个 coin 不累计多次通行许可,这是例子的明确 policy。
完整 file,含 package 与 imports。和 ZIP 中的代码逐字一致。
pattern.go · 25 lines
packagestateimport"errors"varErrLocked=errors.New("gate is locked")typemodeinterface{coin(*Gate)pass(*Gate)error}typeGatestruct{currentmode}funcNew()*Gate{return&Gate{current:locked{}}}func(g*Gate)Coin(){g.current.coin(g)}func(g*Gate)Pass()error{returng.current.pass(g)}typelockedstruct{}func(locked)coin(g*Gate){g.current=unlocked{}}func(locked)pass(*Gate)error{returnErrLocked}typeunlockedstruct{}func(unlocked)coin(*Gate){}func(unlocked)pass(g*Gate)error{g.current=locked{};returnnil}
go test -v ./state在解压后的 gof-min 目录执行
验证 behavior,而不只是能 compilation
test 依次检查拒绝、投入 coin、允许、再次拒绝。第二个 coin 不累计多次通行许可,这是例子的明确 policy。
go test -v ./state在解压后的 gof-min 目录执行
pattern_test.go
packagestateimport("errors""testing")funcTestBehaviorChangesWithInternalState(t*testing.T){g:=New()iferr:=g.Pass();!errors.Is(err,ErrLocked){t.Fatal(err)}g.Coin()g.Coin()iferr:=g.Pass();err!=nil{t.Fatal(err)}iferr:=g.Pass();!errors.Is(err,ErrLocked){t.Fatal("did not return to locked state")}}
Go adaptationtext/template lexer 定义 stateFn func(*lexer) stateFn;nextItem 执行 state(l),lexText 返回下一个 state function,例如 lexLeftDelim。它把 state behavior 放进 function,而不是 lab 的 state object,是自然的 Go adaptation。
// stateFn represents the state of the scanner as a function that returns the next state.typestateFnfunc(*lexer)stateFn// lexer holds the state of the scanner.typelexerstruct{namestring// the name of the input; used only for error reportsinputstring// the string being scannedleftDelimstring// start of action markerrightDelimstring// end of action markerposPos// current position in the inputstartPos// start position of this itematEOFbool// we have hit the end of input and returned eofparenDepthint// nesting depth of ( ) exprslineint// 1+number of newlines seenstartLineint// start line of this item
// nextItem returns the next item from the input.// Called by the parser, not in the lexing goroutine.func(l*lexer)nextItem()item{l.item=item{itemEOF,l.pos,"EOF",l.startLine}state:=lexTextifl.insideAction{state=lexInsideAction}for{state=state(l)ifstate==nil{returnl.item}}}
funclexText(l*lexer)stateFn{ifx:=strings.Index(l.input[l.pos:],l.leftDelim);x>=0{ifx>0{l.pos+=Pos(x)// Do we trim any trailing space?trimLength:=Pos(0)delimEnd:=l.pos+Pos(len(l.leftDelim))ifhasLeftTrimMarker(l.input[delimEnd:]){trimLength=rightTrimLength(l.input[l.start:l.pos])}l.pos-=trimLengthl.line+=strings.Count(l.input[l.start:l.pos],"\n")i:=l.thisItem(itemText)l.pos+=trimLengthl.ignore()
packagestrategyimport"slices"typeLessfunc(a,bint)bool// Sorted accepts a strict weak ordering and leaves the input unchanged.funcSorted(input[]int,lessLess)[]int{result:=slices.Clone(input)slices.SortFunc(result,func(a,bint)int{ifless(a,b){return-1}ifless(b,a){return1}return0})returnresult}
// BackOff is a backoff policy for retrying an operation.typeBackOffinterface{// NextBackOff returns the duration to wait before retrying the operation,// backoff.Stop to indicate that no more retries should be made.//// Example usage://// duration := backoff.NextBackOff()// if duration == backoff.Stop {// // Do not retry operation.// } else {// // Sleep for duration and retry operation.// }//NextBackOff()time.Duration
// Stop retrying if context is cancelled.ifcerr:=context.Cause(ctx);cerr!=nil{returnres,&RetryError{LastErr:lastErr,Cause:cerr}}// Calculate next backoff duration.next:=args.BackOff.NextBackOff()ifnext==Stop{returnres,&RetryError{LastErr:lastErr,Cause:ErrExhausted}}// Reset backoff if a RetryAfterError requested a specific delay.ifretryAfter!=nil{next=retryAfter.Durationargs.BackOff.Reset()
packagetemplatemethodimport("context""fmt")typeStepsinterface{Load(context.Context)([]byte,error)Transform([]byte)([]byte,error)Save(context.Context,[]byte)error}// Run owns ordering and failure policy; injected Steps only fill the hooks.funcRun(ctxcontext.Context,sSteps)error{iferr:=ctx.Err();err!=nil{returnerr}raw,err:=s.Load(ctx)iferr!=nil{returnfmt.Errorf("load: %w",err)}value,err:=s.Transform(raw)iferr!=nil{returnfmt.Errorf("transform: %w",err)}iferr:=ctx.Err();err!=nil{returnerr}iferr:=s.Save(ctx,value);err!=nil{returnfmt.Errorf("save: %w",err)}returnnil}
// A Visitor's Visit method is invoked for each node encountered by [Walk].// If the result visitor w is not nil, [Walk] visits each of the children// of node with the visitor w, followed by a call of w.Visit(nil).typeVisitorinterface{Visit(nodeNode)(wVisitor)}funcwalkList[NNode](vVisitor,list[]N){for_,node:=rangelist{Walk(v,node)}}// TODO(gri): Investigate if providing a closure to Walk leads to// simpler use (and may help eliminate Inspect in turn).
default:panic(fmt.Sprintf("ast.Walk: unexpected node type %T",n))}v.Visit(nil)}typeinspectorfunc(Node)boolfunc(finspector)Visit(nodeNode)Visitor{iff(node){returnf}returnnil