SEO是件贼有意思的事情

这两天迷上了SEO。真心看不起百度的竞价排名,但作为一个商业网站,赚钱是一件无可厚非的事情。只做活雷锋,没有大金主是做不长的。做完功课后,发现百度和google的SEO策略又不相同,几乎是无法通用。百度目前占据着国内搜索市场70%的市场份额,虽然不齿百度的龌龊之举,但也只能沉下心来好好琢磨琢磨百度的SEO策略。以前没有接触过SEO,不懂这里面的水。这两天着了迷想好好研究研究SEO,才发现原来内容是否原创,网页类型(静态/动态),关键字覆盖率,robots是否满足要求,外链质量,网站结构都会影响到最终的排名。当然这里说的都是明面上的技术操作,没提那些个灰色产业。既然称之为灰色产业了,没有老司机带着,也很难明白是怎么运作的。问了一些买过SEO优化的朋友得知,有的SEO优化立竿见影,基本一周后就能冲到前10位,但不续费后,马上下来(肯定有猫腻,但外行人还就是看不懂)。有的SEO会承诺保持多长时间,结果可想而知,这个排名会逐渐逐渐下降。以前感觉写代码是个技术活,最近一段时间越来越发现,写代码真心是个简单的活,这些个说不清道不明,又能挣钱的才是技术活。这几天有时间了,再看看SEO,看能不能把自己的博客也冲到前10名去。

但在冲前十之前,先坚持把这个系列写完了。

<朝鲜三胖哥> 那一节,聊了一嘴,从普通类型转换到interface{}是隐式转换。这节就说一下interface{}这个类型吧,要不总感觉内容少一块。反正也是入门级的书,聊嘛不是聊呀。我准备写完了以后,出一个视频系列,那会就改叫<golang脱口秀>。如果你有好的段子,好的想法或者要推广嘛东西,欢迎发邮件给我(ztao8607@gmail.com),现在有一个影视公司已经下定了,其他人抓紧哦。

Golang中的interface{}有两层含义,一层是单纯的interface{},可以理解成Java的Object基类。因此所有的数据类型都可以转化成interface{}。另外一层含义,就是接口类。这个接口类和Java的接口类又几乎差不多,都是只声明一些方法,而不具体实现。其实第一层里面的interface{}可以理解成特殊的接口类,这个接口类里面没有声明任何方法。所以两层含义可以统一成一个事物。

如果直接把Java里面的接口含义拿过来,这就没有意思了,也就不用写Golang里面的接口类了。Golang的接口和Java的接口说到底本质相同,但模样千秋。下面来看Golang中如何声明一个接口:

  1. type interface_name interface {
  2. method_name1 [return_type]
  3. method_name2 [return_type]
  4. method_name3 [return_type]
  5. ...
  6. method_namen [return_type]
  7. }

在声明struct时候,是type name struct。这里声明接口,是用type name interface,所以不用混淆了。在struct里面,都是各个成员属性,而在interface里面,就都是各个成员函数。从这点来看,Golang和Java的interface声明区别不大,大的区别在如何使用上面,Golang的使用要比Java灵活太多了。用事实来说话,看下面:

  1. type Shape interface {
  2. area() float64
  3. }
  4. func getArea(shape Shape) float64 {
  5. return shape.area()
  6. }

声明了一个Shape的interface,里面有一个成员函数,是area,返回一个float64。而getArea只接受Shape接口类型。好,该上实现类了。

  1. type Circle struct {
  2. x,y,radius float64
  3. }
  4. func(circle Circle) area() float64 {
  5. return math.Pi * circle.radius * circle.radius
  6. }

现在Circle类就实现了刚才Shape的接口。不信,自己敲个代码,验证一下:

  1. circle := Circle{x:0,y:0,radius:5}
  2. fmt.Printf("Circle area: %f\n",getArea(circle))

嘿,一准儿能给你输出结果。

如果只有一个实现类,那就体现不出接口的优势了。再上一个实现类:

  1. type Rectangle struct {
  2. width, height float64
  3. }
  4. func(rect Rectangle) area() float64 {
  5. return rect.width * rect.height
  6. }
  7. rectangle := Rectangle {width:10, height:5}
  8. fmt.Printf("Rectangle area: %f\n",getArea(rectangle))

瞅瞅,又不一样了吧。现在Rectangle和Circle都实现了Shape接口类。怎么实现的?如果你是从Java转过来的,一定会默认去找extend关键字,瞅准了,上面可没有extend关键字。

实现的秘密就在于Rectangle和Circle两个类都有area() float64这个函数。所以在Golang当中,如何实现一个接口类?那就是把接口类中的函数都实现了,就成。

无论接口类中定义了几个函数,都实现了。如果用不着,那就写个函数声明,里面加上空实现就可以。照这个逻辑,一个实现类理论上岂不是能实现无限个接口? 恩,是的,you are right!是这样的!

如果一个项目中,代码太多了,或者交接了好几把手,等你分析代码的时候,你都不知道这个类实现了多少个接口。正所谓有利有弊,Golang中接口的灵活性可以大大减少代码量,减少耦合性。但也反过来降低了可读性,举个例子,如果程序出了bug,而这个bug恰恰出现在一个接口函数中,看代码的时候,你都不知道是哪个实现类出的问题。别说使用go的debug工具,在线上很少会有通过debug打断点进行调试的机会,一来线上环境这么容易进行debug,就说明太不安全了。理论上生产环境中的代码都不应该包含debug信息。二来,有些问题只有在满足一定条件,例如大流量,高并发,某些特殊请求下才能复现,贸然使用debug,已经破坏复现环境了,半天都找不到问题。所以建议在写代码的时候,一定要记得输出日志,最好能在关键节点输出尽可能的详细日志。

Golang中的接口基本使用方法就是这些,如果你有Java基础那么就没有学习难度了。如果没有,也没关系,多写几个代码也就差不多了。下面来看一下实际环境中经常使用到的接口:

  1. type error interface {
  2. Error() string
  3. }

没错,是Error。在此之前,我们都没有考虑如果代码出错了怎么办?所以这个时候就补上这个漏洞。Golang没有try.. catch.. finally。所有的错误都是通过error来处理的,经典的代码如下:

  1. err := xxxxxx
  2. if err != nil {
  3. ....
  4. }

基本就是这样,再高逼格的,就是把err在封装成各种类型。但基本都是这样一套处理方式。Golang也允许自己创建error信息,比如:

  1. func Sqrt(value float64)(float64, error) {
  2. if(value < 0){
  3. return 0, errors.New("Math: negative number passed to Sqrt")
  4. }
  5. return math.Sqrt(value)
  6. }

用户可以通过errors.New()来创建自带业务逻辑的错误信息。这方面就不多展开了,因为每个team都有自己的编码规范,对error的处理方式也千奇百怪,没法统一说明。但处理模式就上面那一套,所以最后来一个实际代码结束Error:

  1. package main
  2. import "errors"
  3. import "fmt"
  4. import "math"
  5. func Sqrt(value float64)(float64, error) {
  6. if(value < 0){
  7. return 0, errors.New("Math: negative number passed to Sqrt")
  8. }
  9. return math.Sqrt(value), nil
  10. }
  11. func main() {
  12. result, err:= Sqrt(-1)
  13. if err != nil {
  14. fmt.Println(err)
  15. } else {
  16. fmt.Println(result)
  17. }
  18. result, err = Sqrt(9)
  19. if err != nil {
  20. fmt.Println(err)
  21. } else {
  22. fmt.Println(result)
  23. }
  24. }