開這款四門轎車,想不被關注都難,奔馳寶馬都得繞道

並不是誰都能觸手可及瑪莎拉蒂Ghibli是特別的它的發展並不存在“延續”曾經的它是跑車雙門兩座跑車和雙門四座跑車分別都是它的前身可是今天的它不再是一款跑車它僅僅是在不同的歷史階段扮演不同的角色它有着一顆3。0T V6的心臟最大馬力350pS最大扭矩500N·m匹配8AT變速箱如果以跑車,甚至超跑的水準去衡量它的動力這還是嫩了一點4門5座的三廂設計讓它顯得更居家儘管它帶有跑車的基

拉風的外觀

奢華的內飾

澎湃的動力

精準的操控

具備這些因素的一定是超跑?

不!也有可能是瑪莎拉蒂Ghibli

極具藝術感的外觀造型

分分鐘能讓你在鬧市中成為焦點

內飾設計舒適、豪華

還有各種真皮面料、高檔木飾等配飾供你選裝

就連車漆顏色、輪轂、剎車卡鉗、音響等配置都可以選配

讓你打造出獨一無二的專屬座駕

正因為它是如此獨特

彷彿時刻都在訴說“生人勿近”

畢竟它的身價

……

並不是誰都能觸手可及

瑪莎拉蒂Ghibli是特別的

它的發展並不存在“延續”

曾經的它是跑車

雙門兩座跑車和雙門四座跑車分別都是它的前身

可是今天的它不再是一款跑車

它僅僅是在不同的歷史階段扮演不同的角色

它有着一顆3.0T V6的心臟

最大馬力350pS

最大扭矩500N·m

匹配8AT變速箱

如果以跑車,甚至超跑的水準去衡量它的動力

這還是嫩了一點

4門5座的三廂設計讓它顯得更居家

儘管它帶有跑車的基本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

這3台不到20萬的中型車,讓開邁騰的年輕人羡慕到想賣車

阿特茲,雖然是一大堆的優點,但都是針對駕駛者而設計的,因此乘客的乘坐體驗就一般了,加上在風噪胎躁方面的控制不是很好,整體的乘坐質感並不高,如果是想給家人更多的舒適體驗,那還是再考慮考慮吧。蒙迪歐,綜合實力很平均,然而在這個價位購車的朋友們多多少少會考慮到油耗這個問題,雖然蒙迪歐的動力數據很好,但在強大動力的加持下,油耗的表現確實是偏高。

今天,跟大家嘮嘮幾款打着運動旗號的中型車,它們分別是:阿特茲,蒙迪歐以及思鉑睿,一看到這幾款車,大家都知道,這些車都有一個通病,就是銷量不高~

其中,蒙迪歐在2017的全年的銷量為:112158台,阿特茲則是:53493台,思鉑睿最少,才19277台,換個說法,這三款車每個月的銷量都是不會破萬的,它們的小眾程度可見一般。

三車外觀如何?

阿特茲自不必說,採用了“魂動”設計語言的它不負“ATENZA”這個寓意為萬眾矚目的稱號。

蒙迪歐,大多朋友戲稱為“小馬丁”的存在,當然,此馬丁非天才DJ Martin Garrix,而是汽車界的紳士-阿斯頓·馬丁,雖然嚴謹的說,它們沒什麼關係,但是蒙迪歐的運動外觀卻是不爭的事實。

思鉑睿,低矮的車身,兇悍的前進氣格柵,這都是它為了區別於傳統舒適性中型車的外觀而設計的。

既然是主打運動,那麼動力是必不可少的較量了

通過三台車的動力系統對比,蒙迪歐在總體上比其它兩款車的動力更加好,畢竟渦輪增壓在這裏佔得便宜不小,但是論起操控,號稱東瀛寶馬的馬自達是三車之中最好的,而屬於買發動機送車殼的本田思鉑睿在這三款車中,發動機與變速箱的匹配程度是最高的。

那麼,運動型車能家用嗎?

在這之前,給大家看看三車的後排空間數據,大家就明白是咋回事了。

阿特茲空間數據

蒙迪歐空間數據

思鉑睿空間數據

通過對比看出,其實三款車型均為中型轎車,在空間方面也不要過於擔心,就算再不濟也是足夠正常使用。

三車的配置上,哪個更優?

給大家帶來的是三款車在相近價位的配置對比,通過對比看出阿特茲的配置更加豐富一點,也更加符合國人買車時“配置豐富”的需求,所以阿特茲在配置對比方面優勢不小。

說了這麼多,這些車都沒有缺點嗎?

當然不是,為你一一道來。

阿特茲,雖然是一大堆的優點,但都是針對駕駛者而設計的,因此乘客的乘坐體驗就一般了,加上在風噪胎躁方面的控制不是很好,整體的乘坐質感並不高,如果是想給家人更多的舒適體驗,那還是再考慮考慮吧。

蒙迪歐,綜合實力很平均,然而在這個價位購車的朋友們多多少少會考慮到油耗這個問題,雖然蒙迪歐的動力數據很好,但在強大動力的加持下,油耗的表現確實是偏高。

思鉑睿,說句老實話,它的裝配水平在這麼多的車之中只能算是一般,而且車內各處的異響也不少,但好在整體的行駛質感做得不錯,在保證一定運動格調的情況下還能兼顧到家庭所需。

對於這三款車型而言,其實取捨也很簡單,假如你是一名極致的操控愛好者,那麼阿特茲絕對可以滿足到你,而針對於蒙迪歐和思鉑睿而言,雖然同樣定位在運動型中級車上,但是它們依然保持着很不錯的舒適性能兼顧到家庭所需,更適合那些以家庭出行為主但又對操控有所要求的朋友。

那麼這幾款車,哪個配置的最值得入手呢?阿特茲,選擇2.5L藍天運動版性價比是最高的,在保證足夠的動力的前提下配置也足夠我們日常所需;而蒙迪歐則可以考慮到1.5T的時尚型,動力能滿足日常所需而且在配置上也能滿足需求;最後輪到思鉑睿,雖然2.4L版本更值得推薦,而且已經自帶了原來的SI運動包圍,但據消息稱2.4L版本已經開始停產,而作為主銷車型的實則為2.0L的低排量版本以及混動版本,而更推薦考慮混動版本。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

讓人又愛又恨的Lombok,到底該不該用

1 簡介

Lombok,印尼的一個島嶼,龍目島。但在Java的世界里,它是一個方便的類庫,能提供很多便利,因此得到許多人的青睞。但也有不少反對聲音。這是為什麼呢?

之前去龍目島拍的日落。

2 Lombok提供的便利

一般我們在Java中用到POJO時,就很容易想到要用Lombok,如VODTODO等。使用Lombok需要安裝對應IDE的插件,同時需要引入依賴:

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.10</version>
  <scope>provided</scope>
</dependency>

舉個例子,如果不用Lombok,實現getter/setterequalshashCodetoString代碼量非常大,如下所示:

package com.pkslow.basic.lombok;

import java.util.Objects;

public class Book {
    private String name;
    private int id;
    private double price;
    private String author;
    private String desc;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return id == book.id &&
                Double.compare(book.price, price) == 0 &&
                Objects.equals(name, book.name) &&
                Objects.equals(author, book.author) &&
                Objects.equals(desc, book.desc);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, id, price, author, desc);
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", price=" + price +
                ", author='" + author + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

而且大部分是樣板代碼,沒有太多實際邏輯。

如果使用Lombok則非常簡單,一個註解@Data就可以完成以上工作(安裝了Lombok插件后才能显示右邊的方法):

Lombok的常用註解有:

@NonNull:用於做空指針異常檢測;

@Cleanup:自動資源關閉;

@Getter/@Setter:自動生成get/set方法;

@ToString:生成toString方法,方便打印調試;

@EqualsAndHashCode:生成equalshashCode方法,注意這兩個應該同時去實現;

@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:構造方法;

@Data:相當於 @ToString+ @EqualsAndHashCode+@Getter+ @Setter +@RequiredArgsConstructor

@Builder:生成builder方法;

@Log:日誌相關:

@CommonsLog:org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Flogger:com.google.common.flogger.FluentLogger.forEnclosingClass();
@JBossLog:org.jboss.logging.Logger.getLogger(LogExample.class);
@Log:java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j:org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2:org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j:org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j:org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
@CustomLog:com.foo.your.LoggerFactory.createYourLogger(LogExample.class);

所以Lombok確實能給我們帶來極大的便利,減少大量重複、無業務邏輯的代碼,代碼顯得比較乾淨;修改了field的名字,不用多處修改;提高開發效率。

3 Lombok所帶來的問題

當然,也有人提出了不同意見:

調試時沒有具體代碼,不方便Debug;

需要強制別人安裝第三方插件,不然會显示報錯;

在檢查測試覆蓋率的時候,無法直觀显示哪些代碼已覆蓋和未覆蓋;

無法按自己意願實現,比如toString方法,有時需要輸出不一樣的String格式,並不一定是按Lombok的實現;

對於一些常用的方法,IDE已經可以自動生成,不需要Lombok一樣可以高效開發。

IDEA沒有提供Builder,但可以通過安裝插件方式使用。

成功安裝后,就能生成Builder代碼了:

4 總結

一開始我是支持使用Lombok的,經過一段時間使用及出現了一些問題后,我還是覺得通過IDE自動生成代碼的方式更適合。畢竟強制要求別人安裝插件這種實在是太野蠻了,而通過IDEA生成代碼,別人不安裝插件也不會有報錯。

另外,多幾行代碼還能體現工作量,哈哈哈哈哈哈……

歡迎訪問南瓜慢說 www.pkslow.com獲取更多精彩文章!

歡迎關注微信公眾號<南瓜慢說>,將持續為你更新…

多讀書,多分享;多寫作,多整理。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

面向對象存儲框架:Obase快速入門

在項目中完成對象建模后,可以使用Obase來進行對象的管理(例如對象持久化),本篇教程將創建一個.NET Core控制台應用,來展示Obase的配置和對象的增刪改查操作。本篇教程旨在指引簡單入門。
本篇教程將以此對象模型展開

class Blog{
    +BlogId:int[文章Id]
    +Url:string[文章地址]
    +Post:sList<Post>[文章評論]
}

class Post{
    +PostId:int[評論Id]
    +Title:string[評論標題]
    +Content:string[評論內容]
    +Blog:Blog[關聯文章]
}

Blog "1"-right-"*"  Post
hide empty member

從NuGet安裝Obase

  • 在VS的NuGet包管理器中添加程序包源:http://nuget.suiyiyun.cn:8081/nuget
  • 在NuGet包管理器中選擇Obase進行安裝

項目搭建

  • 打開 Visual Studio
  • 單擊“創建新項目”
  • 選擇帶有 C# 標記的“控制台應用 (.NET Core)” ,然後單擊“下一步”
  • 輸入“ObaseTutorial” 作為名稱,然後單擊“創建”
  • 添加對freep.Obase.dll的引用

定義領域實體類

	/// <summary>
    /// 文章
    /// </summary>
    public class Blog
    {
        private int blogId;
        private string url;
        private List<Post> posts;

        /// <summary>
        /// 文章Id
        /// </summary>
        public int BlogId { get => blogId; set => blogId = value; }
        /// <summary>
        /// 文章地址
        /// </summary>
        public string Url { get => url; set => url = value; }
       /// <summary>
       /// 文章評論(注意:關聯引用屬性需要定義為virtual)
       /// </summary>
        public virtual List<Post> Posts { get => posts; set => posts = value; }
    }

   /// <summary>
    /// 文章評論
    /// </summary>
    public class Post
    {
        private int postId;
        private string title;
        private string content;
        private int blogId;
        private Blog blog;

        /// <summary>
        /// 評論Id
        /// </summary>
        public int PostId { get => postId; set => postId = value; }
        /// <summary>
        /// 評論標題
        /// </summary>
        public string Title { get => title; set => title = value; }
        /// <summary>
        /// 評論內容
        /// </summary>
        public string Content { get => content; set => content = value; }
        /// <summary>
        /// 文章Id
        /// </summary>
        public int BlogId { get => blogId; set => blogId = value; }
        /// <summary>
        /// 關聯文章(注意:關聯引用屬性需要定義為virtual)
        /// </summary>
        public virtual Blog Blog { get => blog; set => blog = value; }
    }

自定義對象上下文

Obase直接與應用程序進行交互的便是ObectContext(對象上下文),項目中可以根據具體情況定義一個或者多個繼承於ObjectContext的自定義對象上下文。

	using freep.Obase;
	using freep.Obase.ExecuteSql;
	using freep.Obase.Odm.Builder;

	/// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
        /// <summary>
        /// 構造函數
        /// </summary>
        public MyContext() : base("user=root;password=;server=localhost;database=ObaseTutorial;SslMode = none;port=3306;", true)
        {
        }
    }

注意:自定義對象上下文通過繼承父類的構造函數設置數據源連接字符串(此處為了演示方便,直接將連接字符串作為參數進行傳遞,實際項目中可以定義到配置文件中)。

配置對象模型

在對象數據模型生成之前,可以對數據源的類型進行設置,以及對象數據模型的配置,配置的類型包括實體類型,關聯類型,關聯引用,關聯端,屬性等的配置,本篇只展示最基本的實體類型,關聯類型,關聯引用的配置。

    /// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
		/// <summary>
        /// 在即將生成對象數據模型並註冊到對象上下文之前調用此方法
        /// </summary>
        /// <param name="modelBuilder">建模器</param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //設置模型映射目標源的類型(默認不設置未SQLServer)
            modelBuilder.HasTargetSourceType(eDataSource.MySql);
            //配置對象數據模型
            this.ModelConfiguratoin(modelBuilder);
            base.OnModelCreating(modelBuilder);
        }
        
        /// <summary>
        /// 配置對象數據模型
        /// </summary>
        /// <param name="modelBuilder">建模器</param>
        protected virtual void ModelConfiguratoin(ModelBuilder modelBuilder)
        {
        	//配置實體型
            var blogCfg = modelBuilder.Entity<Blog>();
            //設置實體型的映射數據表
            blogCfg.ToTable("Blog");
            //設置實體型的標識屬性
            blogCfg.HasKeyAttribute(p => p.BlogId);
            //設置實體型的標識屬性為自增
            blogCfg.HasKeyIsSelfIncreased(true);

            //配置實體型
            var postCfg = modelBuilder.Entity<Post>();
            //設置實體型的映射數據表
            postCfg.ToTable("Post");
            //設置實體型的標識屬性
            postCfg.HasKeyAttribute(p => p.PostId);
            //設置實體型的標識屬性為自增
            postCfg.HasKeyIsSelfIncreased(true);

            //配置對象間隱式關聯類型
            var blogAssPostCfg = modelBuilder.Association<Blog, Post>();
            //設置關聯類型的映射數據表
            blogAssPostCfg.ToTable("Post");
            //設置關聯映射端1(參照方)的鍵屬性以及在關聯表中映射的字段
            blogAssPostCfg.AssociationEnd<Blog>("End1").HasMapping("BlogId", "BlogId");
            //設置關聯映射端2(被參照方)的鍵屬性以及在關聯表中映射的字段
            //注意:HasDefaultAsNew方法設置一個值,該值指示是否把關聯端對象默認視為新對象。當該屬性為true時,如果關聯端對象未被顯式附加到上下文,該對象將被視為新對象實施持久化。
            blogAssPostCfg.AssociationEnd<Post>("End2").HasMapping("PostId", "PostId").HasDefaultAsNew(true);

            //配置實體類型的關聯引用屬性
            //參數一:關聯引用屬性的名稱 參數二:關聯引用是否具有多重性
            //注:此處在配置Blog實體與Post實體關聯引用屬性Posts
            var blogRefPosts = blogCfg.AssociationReference<Blog, Post>("Posts", true);
            //設置關聯引用的本端
            blogRefPosts.HasLeftEnd("End1");
            //設置關聯引用的對端
            blogRefPosts.HasRightEnd("End2");
            //設置關聯引用屬性延遲加載
            blogRefPosts.HasEnableLazyLoading(true);

            //配置實體類型的關聯引用屬性
            //參數一:關聯引用屬性的名稱 參數二:關聯引用是否具有多重性
            //注:此處在配置Post實體與Blog實體關聯引用屬性Blog
            var postRefBlog = postCfg.AssociationReference<Blog, Post>("Blog", false);
            //設置關聯引用的本端(注意此處Post是作為本端的)
            postRefBlog.HasLeftEnd("End2");
            //設置關聯引用的對端
            postRefBlog.HasRightEnd("End1");
        }
	}

定義對象集

最終對對象的操作和訪問是通過對象上下文提供的對象集,此處我們定義文章和文章評論對象集:

    /// <summary>
    /// 自定義對象上下文
    /// </summary>
    public class MyContext : ObjectContext
    {
        /// <summary>
        /// 文章對象集
        /// </summary>
        public ObjectSet<Blog> Blogs { get; set; }

        /// <summary>
        /// 文章評論對象集  
        /// </summary>
        public ObjectSet<Post> Posts { get; set; }
    }

對象的創建、讀取、更新和刪除

實例化對象上下文
var myContext = new MyContext();
創建
//實例化對象
Blog blog = new Blog()
{
    Url = "https://www.yuque.com/geekfish/obase/getting-started",
    Posts = new List<Post>() {
        new Post (){  Title= "請問Obase怎麼安裝?", Content = "暫時只提供dll文件"}
    }
};
//將對象附加到對象上下文
myContext.Blogs.Attach(blog);
//將對象保存到數據源
myContext.SaveChanges();
讀取
using System.Linq;

//從持久化源查詢數據
Blog firstBlog = myContext.Blogs.OrderBy(p => p.Url).First();
//訪問關聯引用屬性
List<Post> posts = firstBlog.Posts;
更新
 //修改屬性
firstBlog.Url = "http://www.test.com/aa.html";
//將對象保存到數據源
myContext.SaveChanges();
刪除
//刪除指定對象
myContext.Blogs.Remove(firstBlog);
//根據條件刪除指定對象
myContext.Blogs.Delete(p => p.BlogId == 1);
//將對象保存到數據源(只有在保存后,數據才真實刪除)
myContext.SaveChanges();

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

技術人員該如何站好最後一班崗?

挑槽、入槽、跳槽,堪稱每個技術人員必奏的三部曲,而這三部曲在職場中來回奏,便構成了程序人生。

鐵打的硬盤,流水的碼農,離職時見人品,作為技術人員該如何做交接,到底該如何站好最後一班崗呢?

 

1. 人品不夠,文檔來湊。

 

從上家公司離職已經 5 年多啦,記得離職大概沒多久,前技術同事微信告訴我:你寫的交接文檔,在會議上公開表揚,讓其它組作為參考。

當時個人感覺沒啥,就是寫了一堆文檔罷了,近期看到其它團隊交接的效果,那麼的不盡人意。而且秉着吐露真心,認真分享的原則,不妨把那些年寫過的交接文檔,逐一呈現給你,萬一能助你積攢人品、升職加薪呢?

1.1. 作為技術人員離職前的交接,編寫交接進展表為了誰?

離職前的交接,非常能展現人品,最重要的原則是:交接時一定要儘力而為。

盡量能打造屬於自己的交接計劃,按照計劃一步一步去落實,並把交接進展維護在 excel 中,如圖中的《交接進展表.xlsx》。

編寫交接進展表,一方面讓大家明確知曉交接的過程與進度,另一方面可供後人按此方式進行無腦式交接(前人栽樹後人乘涼)。

1.2. 作為技術人員離職前的交接,編寫XX系統_新手入門文檔為了誰?

離職前的交接,希望都能編寫新手入門之類的傻瓜式文檔,該文檔編寫是個一勞永逸的事情。

倘若後續接手你的是一個新同事,那麼就更有價值,按照入門文檔,一步一步就能上手開發、提測、上線,這樣的文檔誰不喜歡?

僅以上面截圖為例,新手入門文檔中包含了系統的簡要說明、功能說明以及功能模塊劃分,可以讓接手的同事對系統有一個全局的認識。

當然,最重要的是要告訴要接手的同事,如何去幹活?文檔中的應用目錄結構介紹以及如何開發、如何編譯、如何提測、如何上線,這幾大塊就顯得很重要。

另外,站在團隊培養人的成本而言,新手入門文檔,不僅僅是為了做好交接,倘若項目組一直就具備該文檔,能夠讓新手快速上手開發業務需求,大概率會降低團隊溝通、培訓的成本。

1.3. 作為技術人員離職前的交接,編寫XX系統_開發生產部署文檔為了誰?

離職前的交接,當接手的同事能夠按照入門文檔開發需求之後,更一步的就是要了解開發、測試、生產部署環境相關的信息。

 

僅以上面截圖為例,開發及生產部署文檔,也可以理解成環境相關的文檔。其主要目的是匯總開發、生產環境部署的機器、應用部署的位置及應用該如何訪問的關鍵信息,讓接手的同事,能夠清楚當需求開發完成時,應用應該如何部署。

1.4. 作為技術人員離職前的交接,編寫XX系統_業務支撐文檔為了誰?

離職前的交接,當接手的同事了解完如何入門開發、開發生產部署環境,接下來就要花大量的時間,去了解支撐的業務。

鑒於支撐的業務會較多,作為接手的同事梳理起來會比較頭疼,那麼一個清晰的文檔索引就很重要。

業務支撐文檔就是把 SVN 或者 Git 上的產品相關的資料,分門別類把路徑整理到文檔中,以便接手的同事查閱,以便進行快速深入。

1.5. 作為技術人員離職前的交接,編寫XX系統_經驗匯總文檔為了誰?

離職前的交接,最重要的是分享前車之鑒,對於要接手的同事而言少走彎路,避免再掉坑,絕對是一筆財富。

僅以上面截圖為例,系統的經驗匯總文檔,主要記錄平時該注意的事項、項目團隊中以往遇到那些坑,以及如何把坑填平的。

有了經驗匯總文檔,無論是接手的同事,還是新招的同事,再去做需求開發,相信同樣問題出錯的概率應該會大幅降低。

不過該經驗文檔離不開一個長期積累的過程,所以程序員要養成一個善於記錄的習慣。

 

2. 人品不夠,分享來湊。

 

離職前的交接,梳理文檔是一方面,隔三差五的組織分享也是必不可少的環節。

如上面截圖所示,主要包含生產部署相關以及業務支撐相關,目的就是把重要的信息,以培訓會議的形式再次同步給大家,讓團隊中的每個人都做到心中有數。

當然,鑒於分享會耗費大家的時間,所以要提前準備好要分享的重要信息,合理安排時間去完成分享。

 

3. 離職之後,保持藕斷絲連。

 

如果你之前負責的是重要項目,即使交接做的很成功,但是之前的老同事,偶爾還會給你打電話諮詢項目的事情,至少會持續一個月甚至更長。

那麼請不要悲傷、憤怒,換個角度去思考,前同事有問題能想到你,說明你在他們心中還是有分量的,或許他們認為你了解的比較透徹,知道問題的解決方案,能夠快速幫其解決問題。

 

今日留情面,他日好相見。

 

互聯網的圈子真的很小,說不定哪一天又在下一家公司相見啦,所以一定要留有情面,把事情交接好,把最後一班崗站好。

最後一班崗站好,大家心中都有你,有機會就會向你拋橄欖枝。

 

曾經的那些人兒,那些事兒。

 

橄欖枝一:發生在 3 年前,上家公司的技術總監去了知名網購平台,電話問我能不能把簡歷發來,是否願意來承擔一些事兒?當我接到電話時,瞬間詫異,技術總監都拿到我手機號啦。

橄欖枝二:發生在去年,上家公司的某位高級經理被挖去了新的公司,擔任 CTO 職位,由於業務擴展,多次問我是不是可以一起搞一搞?

 

估計很多人都接過橄欖枝,聊橄欖枝不是為了裝 B,只是想反思一下橄欖枝背後,是不是和之前交接的過程有點關係呢?

 

4. 鐵打的硬盤,流水的碼農。

 

作為技術人員請不要:這個我交接給他啦,你直接去找他吧!

作為技術人員請不要:惡意製造交接困難,讓交接難上加難!

作為技術人員請做到:站好最後一班崗,今日留情面,他日好相見。

 

本次主要分享了之前交接時的思路以及寫過的一些文檔,如果感覺有一絲參考價值,那請拿去在團隊中實踐,沉澱下來的都是財富。

好了,分享就到這裏,希望對你有幫助。一起聊技術、談業務、噴架構,少走彎路,不踩大坑。歡迎關注「一猿小講」,會持續輸出原創精彩分享,敬請期待!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Vue數據雙向綁定原理

Vue數據雙向綁定

Vue是通過數據劫持的方式來實現數據雙向數據綁定的,其中最核心的方法便是通過Object.defineProperty()來實現對屬性的劫持,該方法允許精確地添加或修改對象的屬性,對數據添加屬性描述符中的gettersetter實現劫持。

描述

運行一個Vue實例並將data打印,可以看到對象中對於msg有了getset,通過他們就可以實現數據的劫持,從而進行數據的更新,在Vuegetset是通過ES5Object.defineProperty()方法定義的,該方法的具體功能可以查閱https://github.com/WindrunnerMax/EveryDay/blob/master/JavaScript/defineProperty.md

<!DOCTYPE html>
<html>
<head>
    <title>數據綁定</title>
</head>
<body>
    <div id="app">
        <div>{{msg}}</div>
    </div> 
</body>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'Data'
        },
        created: function() {
            console.log(this.$data); //{__ob__: Observer} 
        }
    })
</script>
</html>
/*
{__ob__: Observer}
    msg: "Data"
    __ob__: Observer {value: {…}, dep: Dep, vmCount: 1}
    get msg: ƒ reactiveGetter()
    set msg: ƒ reactiveSetter(newVal)
    __proto__: Object
*/

分析實現

Vue的雙向數據綁定,簡單點來說分為以下三個部分:

  • Observer: 這裏的主要工作是遞歸地監聽對象上的所有屬性,在屬性值改變的時候,觸發相應的Watcher
  • Watcher: 觀察者,當監聽的數據值修改時,執行響應的回調函數,在Vue裏面的更新模板內容。
  • Dep: 鏈接ObserverWatcher的橋樑,每一個Observer對應一個Dep,它內部維護一個數組,保存與該Observer相關的Watcher

根據上面的三部分實現一個功能非常簡單的Demo,實際Vue中的數據在頁面的更新是異步的,且存在大量優化,實際非常複雜。
首先實現Dep方法,這是鏈接ObserverWatcher的橋樑,簡單來說,就是一個監聽者模式的事件總線,負責接收watcher並保存。其中subscribers數組用以保存將要觸發的事件,addSub方法用以添加事件,notify方法用以觸發事件。

function __dep(){
    this.subscribers = [];
    this.addSub = function(watcher){
        if(__dep.target && !this.subscribers.includes(__dep.target) ) this.subscribers.push(watcher);
    }
    this.notifyAll = function(){
        this.subscribers.forEach( watcher => watcher.update());
    }
}

Observer方法就是將數據進行劫持,使用Object.defineProperty對屬性進行重定義,注意一個屬性描述符只能是數據描述符和存取描述符這兩者其中之一,不能同時是兩者,所以在這個小Demo中使用gettersetter操作的的是定義的value局部變量,主要是利用了let的塊級作用域定義value局部變量並利用閉包的原理實現了gettersetter操作value,對於每個數據綁定時都有一個自己的dep實例,利用這個總線來保存關於這個屬性的Watcher,並在set更新數據的時候觸發。

function __observe(obj){
    for(let item in obj){
        let dep = new __dep();
        let value = obj[item];
        if (Object.prototype.toString.call(value) === "[object Object]") __observe(value);
        Object.defineProperty(obj, item, {
            configurable: true,
            enumerable: true,
            get: function reactiveGetter() {
                if(__dep.target) dep.addSub(__dep.target);
                return value;
            },
            set: function reactiveSetter(newVal) {
                if (value === newVal) return value;
                value = newVal;
                dep.notifyAll();
            }
        });
    }
    return obj;
}

Watcher方法傳入一個回調函數,用以執行數據變更后的操作,一般是用來進行模板的渲染,update方法就是在數據變更后執行的方法,activeRun是首次進行綁定時執行的操作,關於這個操作中的__dep.target,他的主要目的是將執行回調函數相關的數據進行sub,例如在回調函數中用到了msg,那麼在執行這個activeRun的時候__dep.target就會指向this,然後執行fn()的時候會取得msg,此時就會觸發msgget(),而get中會判斷這個__dep.target是不是空,此時這個__dep.target不為空,上文提到了每個屬性都會有一個自己的dep實例,此時這個__dep.target便加入自身實例的subscribers,在執行完之後,便將__dep.target設置為null,重複這個過程將所有的相關屬性與watcher進行了綁定,在相關屬性進行set時,就會觸發各個watcherupdate然後執行渲染等操作。

function __watcher(fn){
    this.update = function(){
        fn();
    }
    
    this.activeRun = function(){
        __dep.target = this;
        fn();
        __dep.target = null;
    }
    this.activeRun();
}

代碼示例

這是上述的小Demo的代碼示例,其中上文沒有提到的__proxy函數主要是為了將vm.$data中的屬性直接代理到vm對象上,兩個watcher中第一個是為了打印並查看數據,第二個是之前做的一個非常簡單的模板引擎的渲染,為了演示數據變更使得頁面數據重新渲染,在這個Demo下打開控制台,輸入vm.msg = 11;即可觸發頁面的數據更改,也可以通過在40行添加一行console.log(dep);來查看每個屬性的dep綁定的watcher

<!DOCTYPE html>
<html>
<head>
    <title>數據綁定</title>
</head>
<body>
    <div id="app">
        <div>{{msg}}</div>
        <div>{{date}}</div>
    </div> 
</body>
<script type="text/javascript">

    var Mvvm = function(config) {
        this.$el = config.el;
        this.__root = document.querySelector(this.$el);
        this.__originHTML = this.__root.innerHTML;

        function __dep(){
            this.subscribers = [];
            this.addSub = function(watcher){
                if(__dep.target && !this.subscribers.includes(__dep.target) ) this.subscribers.push(watcher);
            }
            this.notifyAll = function(){
                this.subscribers.forEach( watcher => watcher.update());
            }
        }


        function __observe(obj){
            for(let item in obj){
                let dep = new __dep();
                let value = obj[item];
                if (Object.prototype.toString.call(value) === "[object Object]") __observe(value);
                Object.defineProperty(obj, item, {
                    configurable: true,
                    enumerable: true,
                    get: function reactiveGetter() {
                        if(__dep.target) dep.addSub(__dep.target);
                        return value;
                    },
                    set: function reactiveSetter(newVal) {
                        if (value === newVal) return value;
                        value = newVal;
                        dep.notifyAll();
                    }
                });
            }
            return obj;
        }

        this.$data = __observe(config.data);

        function __proxy (target) {
            for(let item in target){
                Object.defineProperty(this, item, {
                    configurable: true,
                    enumerable: true,
                    get: function proxyGetter() {
                        return this.$data[item];
                    },
                    set: function proxySetter(newVal) {
                        this.$data[item] = newVal;
                    }
                });
            }
        }

        __proxy.call(this, config.data);

        function __watcher(fn){
            this.update = function(){
                fn();
            }
            
            this.activeRun = function(){
                __dep.target = this;
                fn();
                __dep.target = null;
            }
            this.activeRun();
        }

        new __watcher(() => {
            console.log(this.msg, this.date);
        })

        new __watcher(() => {
            var html = String(this.__originHTML||'').replace(/"/g,'\\"').replace(/\s+|\r|\t|\n/g, ' ')
            .replace(/\{\{(.)*?\}\}/g, function(value){ 
                return  value.replace("{{",'"+(').replace("}}",')+"');
            })
            html = `var targetHTML = "${html}";return targetHTML;`;
            var parsedHTML = new Function(...Object.keys(this.$data), html)(...Object.values(this.$data));
            this.__root.innerHTML = parsedHTML;
        })

    }

    var vm = new Mvvm({
        el: "#app",
        data: {
            msg: "1",
            date: new Date(),
            obj: {
                a: 1,
                b: 11
            }
        }
    })

</script>
</html>

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://www.jianshu.com/p/255d4dec710a
https://www.jianshu.com/p/c8186e9e027b
https://www.cnblogs.com/wangjiachen666/p/9883916.html
https://blog.csdn.net/wangshu696/article/details/84570886
https://blog.csdn.net/qq_43051529/article/details/82877673
https://github.com/liutao/vue2.0-source/blob/master/%E5%8F%8C%E5%90%91%E6%95%B0%E6%8D%AE%E7%BB%91%E5%AE%9A.md

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

曹工改bug:centos下,mongodb開機不能自啟動,systemctl、rc.local都試了,還是不行,要不要放棄?

問題背景

最近裝個centos 7.6的環境,其中,基礎環境包括,redis、nginx、mongodb、fastdfs、mysql等,其中,自啟動使用的是systemctl,其他幾個組件,都沒啥問題,唯獨,這個mongodb,是死活啟動不了。

但是,我這裏說的,不是啟動不了,如果直接在shell里敲:

systemctl start  mongod.service

是沒啥問題的,是可以啟動的。

mongod.service大致如下,各文件夾的權限也已經仔細檢查過,應該是沒毛病:

[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target
Documentation=https://docs.mongodb.org/manual

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=-f /etc/mongod.conf"
ExecStart=/usr/bin/mongod $OPTIONS
ExecStartPre=/usr/bin/mkdir -p /var/run/mongodb
ExecStartPre=/usr/bin/chown mongod:mongod /var/run/mongodb
ExecStartPre=/usr/bin/chmod 0755 /var/run/mongodb
PermissionsStartOnly=true
PIDFile=/var/run/mongodb/mongod.pid
Type=forking
# file size
LimitFSIZE=infinity
# cpu time
LimitCPU=infinity
# virtual memory size
LimitAS=infinity
# open files
LimitNOFILE=64000
# processes/threads
LimitNPROC=64000
# locked memory
LimitMEMLOCK=infinity
# total threads (user+kernel)
TasksMax=infinity
TasksAccounting=false
# Recommended limits for for mongod as specified in
# http://docs.mongodb.org/manual/reference/ulimit/#recommended-settings

[Install]
WantedBy=multi-user.target

後邊換成了rc.local方式:

/etc/rc.d/rc.local  

#!/bin/bash
/usr/bin/mongod --fork -f /etc/mongod.conf &

但是,依然不行。

這两天,同事也在斷斷續續在弄,大家手裡有其他事,這塊暫時放下了。

轉機:strace命令

我一般瀏覽器開的tab比較多,有個幾天前的tab,是關於strace的,我當時主要是想:找一個命令,可以監控某個進程的網絡請求。

本來可以用tcpdump,但是,這個不是針對某個進程的,只能通過端口過濾,一般情況下,用端口過濾也足夠了,但是,總是覺得不爽。

然後找到了這個鏈接:

https://askubuntu.com/questions/11709/how-can-i-capture-network-traffic-of-a-single-process

裏面提到了strace可以做到。試了下,確實完美解決了我的問題。

比如說,我可以attach到某個進程,然後調用進程的某個接口,讓該進程調用某個微服務,然後看看我們能不能抓到:

(看不清可在單獨tab查看,這個是java應用,向註冊中心eureka發送的心跳,可以發現,完美抓到了)

strace的使用

可參考我的這篇簡單介紹:

Linux下,如何監控某個進程到底向哪個地址發起了網絡調用

大家也可以直接在服務器上直接執行以下shell,查看幫助文檔:

yum install strace
man strace

其主要支持兩種方式,一種是直接使用strace來包裝某個命令,使用strace來啟動該命令;另一種,就是上面說的,attach到某個已經在運行中的進程。

然後,我想到,既然strace可以監控系統調用,那麼,監控下mongodb吧,這裏可以用strace來包裝命令,我試着把命令換成了如下的樣子:

strace -tt -s 10000 -o  mongo.txt /usr/bin/mongod --fork -f /etc/mongod.conf

這塊命令,分兩塊看。

strace -tt -s 10000  -o  mongo.txt 

這部分是strace的參數,

-tt 打印時間

-s 設置字符串的長度,否則具體內容显示不出來

-o 將結果輸出到文件

另外部分,就是mongo的命令了:

/usr/bin/mongod --fork  /etc/mongod.conf

比較正常情況下,和異常情況下的strace輸出日誌

在同事建議下,我們先直接在shell中執行了以下命令:

strace -tt -s 10000  -o  mongo.txt /usr/bin/mongod --fork -f /etc/mongod.conf

然後,得到了mongo.txt。這個是正常的文件。

然後,我們reboot了服務器,然後不清楚文件生成到哪裡了,直接find查找了一把,然後找到后,把這兩個文件,都存到了pc上,用beyond compare進行對比。

下面是對比結果:

建議大圖查看。

可以發現,有問題的文件里,在打開以下文件時,報了錯,提示沒有權限:

/sys/fs/cgroup/memory/memory.limit_in_bytes   EACCES (Permission denied)

然後,我們只是知道了,有這個現象,但不知道為啥,然後就開始了一頓漫無邊際的面向搜索引擎找問題。找了半天,沒啥收穫。

後邊我們就決定再去看看官網,看看官網有沒有說,怎麼才是正統的服務自啟動方式(已經快放棄了。。。)

柳暗花明

結果在官網的如下鏈接:

https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/#install-mongodb-community-edition

看到如下一段話,說SELinux,默認不讓mongo訪問/sys/fs/cgroup:

然後我一看,這個目錄有點熟悉啊,上面報沒有權限的文件,不就這個目錄下嗎?

/sys/fs/cgroup/memory/memory.limit_in_bytes   EACCES (Permission denied)

知道原因就好說了,原來是SELinux,我們這邊比較暴力,直接把這個關閉了。

關閉方式:https://www.cnblogs.com/activiti/p/7552677.html

總結

在華為的時候,組裡的大佬們有一句話:辦法總比困難多。

那時感覺,這也太雞湯了。。。

我現在也有這個感覺,每次在快要放棄時,問題結果被解決了,有點意思。

另外一個感悟:方法比結果重要

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

非洲大城空污直逼北京 禍首是二手車

摘錄自2020年11月4日科技新報報導

非洲國家蒙受空污之苦,在 PM2.5 方面最大禍首是從先進國家進口的二手車,隨著人民對汽車需求愈來愈大,城市空污問題成為難解之題。

QUARTZ 報導,在 2015~2018 年間,從歐盟、日本和美國出口的 1400 萬輛二手車中,約有 80% 流向中低收入國家。非洲是全球每年二手車進口量最多的國家,隨著這種缺乏檢驗標準的老舊汽車進入非洲,加劇城市空氣污染問題。譬如 2017 年與 2018 年荷蘭出口 3.5 萬台到西非的汽車當中,大部分都是 16~20 年的車,由於缺乏監管標準來衡量進口汽車的安全性和品質,這些車很多都低於歐洲安全和排放要求,造成汽車排放物成為非洲最大城市空污的重要來源。

接下來的 30 年中,非洲的人口增長將比其他任何地方都快,污染情況幾乎無法逆轉。最新研究估計,到 2030 年非洲大陸僅發電廠和車輛排放的化石燃料每年將導致近 5 萬人死亡。預計到 2030 年,二氧化硫和氮氧化物的年排放量將比 2012 年的水平多一倍。

污染治理
國際新聞
非洲
空氣污染
二手車

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

日本首見 蝙蝠糞便病毒基因近似武漢肺炎病毒

摘錄自2020年11月7日中央社報導

日本東京大學等組成的研究團隊公布,從日本國內的蝙蝠糞便中,發現基因近似新型冠狀病毒的病毒,是日本首度從國內野生動物上發現。這次發現的冠狀病毒並不會傳染給人類。

日本放送協會(NHK)報導,專精獸醫學的東京大學研究所副教授村上晉等人組成的研究團隊,對外公布這項調查結果。研究團隊詳細調查7年前在日本國內洞窟採集到的「角菊頭蝠」(Rhinolophus cornutus)糞便中所含病毒基因時,發現跟新型冠狀病毒有81.5%一致。研究團隊透過實驗確認,這種冠狀病毒不會傳染給人類。

目前已知從中國的蝙蝠等身上,發現跟新型冠狀病毒基因逾95%一致的病毒,但日本研究團隊表示,這應是日本國內發現到跟新型冠狀病毒基因最接近的病毒。村上說,雖然冠狀病毒被認為只有極少部分具有危險,但也不能否定日本國內存在著能傳染給人類的種類,必須調查野生動物,盡速掌握實際狀態。

生物多樣性
國際新聞
日本
蝙蝠
冠狀病毒
武漢肺炎
動物與大環境變遷

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

美國綠色新政與新任總統拜登的氣候計畫

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化