GO Action攻读

444 阅读1分钟

关于Go语言的介绍

优势

智能的编译器,并简化了解决依赖的算法,最终提供了更快的编译速度
支持并发:goroutine,通道
类型系统:组合代替继承,接口对行为建模
内存管理

Go Playground

http://paly.golang.org

Go程序开发

项目结构

- sample 
    - data
        data.json -- 包含一组数据源
    - matchers 
        rss.go -- 搜索rss源的匹配器
     - search 
         default.go -- 搜索数据用的默认匹配器 
         feed.go -- 用于读取json数据文件 
         match.go -- 用于支持不同匹配器的接口
         search.go -- 执行搜索的主控制逻辑
     main.go -- 程序的入口

main.go

  package main
  import ( 
      "log" 
      "os" 
      _ "github.com/goinaction/code/chapter2/sample/matchers" 
      "github.com/goinaction/code/chapter2/sample/search" 
  ) 
  
  func init() { 
     log.SetOutput(os.Stdout) 
  } 
  func main() { 
      search.Run("president")
  }

search.go

package search 
import ( 
    "log" 
    "sync" 
 ) 
 
 var matchers = make(map[string]Matcher)
 
 func Run(searchTerm string) { 
     feeds, err := RetrieveFeeds() 
     if err != nil { 
         log.Fatal(err)
      } 
      results := make(chan *Result) 
      var waitGroup sync.WaitGroup 
      waitGroup.Add(len(feeds)) 
      for _, feed := range feeds { 
          matcher, exists := matchers[feed.Type] 
          if !exists {
              matcher = matchers["default"] 
           } 
           go func(matcher Matcher, feed *Feed) { 
               Match(matcher, feed, searchTerm, results) 
               waitGroup.Done() 
            }(matcher, feed) 
       } 
       go func() { 
           waitGroup.Wait() 
           close(results) 
       }() 
       
       Display(results) 
    }
    
     func Register(feedType string, matcher Matcher) {
          if _, exists := matchers[feedType]; exists { 
              log.Fatalln(feedType, "Matcher already registered") 
           } 
           log.Println("Register", feedType, "matcher")
           matchers[feedType] = matcher 
      }

feed.go

  package search
  import ( 
      "encoding/json" 
      "os" 
  )
  const dataFile = "data/data.json"
  type Feed struct { 
      Name string `json:"site"` 
      URI string `json:"link"`
      Type string `json:"type"` 
  }
  func RetrieveFeeds() ([]*Feed, error) { 
      file, err := os.Open(dataFile) 
      if err != nil { 
          return nil, err 
      } 
      defer file.Close() 
      var feeds []*Feed 
      err = json.NewDecoder(file).Decode(&feeds)
      return feeds, err 
  }

data.json

  [ { "site" : "npr",       "link" : "http://www.npr.org/rss/rss.php?id=1001",      "type" : "rss" },    { "site" : "cnn",        "link" : "http://rss.cnn.com/rss/cnn_world.rss",        "type" : "rss" },    {"site" : "foxnews",        "link" : "http://feeds.foxnews.com/foxnews/world?format=xml",       "type" : "rss" },     { "site" : "nbcnews",      "link" : "http://feeds.nbcnews.com/feeds/topstories",     "type" : "rss" }    ]

match.go

  package search 
  import ( 
      "log" 
  )
  type Result struct {
      Field string 
      Content string 
  }
  type Matcher interface { 
       Search(feed *Feed, searchTerm string) ([]*Result, error) 
   }
 func Match(matcher Matcher, feed *Feed, searchTerm string, results chan<- *Result) { 
         searchResults, err := matcher.Search(feed, searchTerm) 
         if err != nil {
              log.Println(err) 
              return 
          }
         for _, result := range searchResults { 
             results <- result 
          } 
    }
   func Display(results chan *Result) {
        for result := range results { 
            fmt.Printf("%s:\n%s\n\n", result.Field, result.Content) 
         } 
   }

default.go

package search 
type defaultMatcher struct{} 
func init() { 
    var matcher defaultMatcher
    Register("default", matcher)  
} 
func (m defaultMatcher) Search(feed *Feed, searchTerm string) ([]*Result, error) { 
    return nil, nil 
 }

rss.go

 package matchers
 import ( 
     "encoding/xml"
     "errors" 
     "fmt"  
     "log"
      "net/http" 
      "regexp" 
      "github.com/goinaction/code/chapter2/sample/search" 
 )
 type ( 
     item struct { 
         XMLName xml.Name `xml:"item"`
         PubDate string `xml:"pubDate"` 
         Title string `xml:"title"` 
         Description string `xml:"description"` 
         Link string `xml:"link"` 
         GUID string `xml:"guid"` 
         GeoRssPoint string `xml:"georss:point"` 
     } 
     image struct { 
         XMLName xml.Name `xml:"image"` 
         URL string `xml:"url"` 
         Title string `xml:"title"` 
         Link string `xml:"link"` 
     } 
     channel struct {
         XMLName xml.Name `xml:"channel"` 
         Title string `xml:"title"` 
         Description string `xml:"description"` 
         Link string `xml:"link"` 
         PubDate string `xml:"pubDate"` 
         LastBuildDate string `xml:"lastBuildDate"` 
         TTL string `xml:"ttl"` 46 Language string `xml:"language"` 
         ManagingEditor string `xml:"managingEditor"` 
         WebMaster string `xml:"webMaster"` 
         Image image `xml:"image"` 
         Item []item `xml:"item"` 
      } 
      rssDocument struct { 
          XMLName xml.Name `xml:"rss"` 
          Channel channel `xml:"channel"` 
      } 
   )
   type rssMatcher struct{}
   func init() { 
        var matcher rssMatcher 
        search.Register("rss", matcher) 
   }
   func (m rssMatcher) Search(feed *search.Feed, searchTerm string) ([]*search.Result,error) {
       var results []*search.Result 
       log.Printf("Search Feed Type[%s] Site[%s] For Uri[%s]\n", feed.Type, feed.Name, feed.URI)
       document, err := m.retrieve(feed)
       if err != nil {
            return nil, err 
       } 
       for _, channelItem := range document.Channel.Item { 
            matched, err := regexp.MatchString(searchTerm, channelItem.Title) 
            if err != nil { 
                 return nil, err 
            } 
            if matched { 
                  results = append(results, &search.Result{ 
                              Field: "Title", 
                              Content: channelItem.Title, 
                          }) 
            }
            matched, err = regexp.MatchString(searchTerm, channelItem.Description) 
            if err != nil { 
                  return nil, err
               } 
               if matched { 
                   results = append(results, &search.Result{ 
                       Field: "Description", 
                       Content: channelItem.Description, 
                      }) 
                } 
            } 
            return results, nil 
        }
   func (m rssMatcher) retrieve(feed *search.Feed) (*rssDocument, error) {
       if feed.URI == "" { 
           return nil, errors.New("No rss feed URI provided") 
        }  
        resp, err := http.Get(feed.URI) 
        if err != nil { 
            return nil, err 
         } 
         defer resp.Body.Close()
         if resp.StatusCode != 200 { 
             return nil, fmt.Errorf("HTTP Response Error %d\n", resp.StatusCode)
          } 
          var document rssDocument 
          err = xml.NewDecoder(resp.Body).Decode(&document) 
          return &document, err
   }

rss示例

<rss xmlns:npr="http://www.npr.org/rss/" xmlns:nprml="http://api" 
    <channel> 
        <title>News</title> 
        <link>...</link> 
       <description>...</description>
       <language>en</language> 
       <copyright>Copyright 2014 NPR - For Personal Use 
       <image>...</image>
       <item>
           <title> Putin Says He'll Respect Ukraine Vote But U.S.
           </title> 
           <description> The White House and State Department have called on the 
           </description>

接口类型的值调用方法

使用指针作为接收者声 明的方法,只能在接口类型的值是一个指针的时候被调用
使用值作为 接收者声明的方法,在接口类型的值为值或者指针时,都可以被调用。

全部流程