我之前说过,我会关闭一切技术分享。这也意味着,我不会以任何形式去探讨技术。
在这里我分享的不是技术,而是思想。如果你从我说的东西里理解出了什么,完全是你自己的理解。
前面先发一段牢骚,不想看的话,直接跳转到后面红字开始的部分。
现在的时间是凌晨4点,看了三个小时的车珠子、男默女泪、我该怎么办的百度软文。我只能说唉,这个社会太浮躁了。或许作为我来讲,努力挣钱,随便找个相亲平台结个婚养育下一代才是正点。但对于我而言,我内心是真的不服输的。有时候特别想问很多事情……凭什么?
1月20日,去了一家公司“面试”,技术总监是曾经做谷歌音乐的。公司的位置让我并不想去,不是太远,相反,其实我骑车不到20分钟的路。不想去的原因是因为地理位置太过尴尬,涉及到家庭原因就不想多说什么了。
公司做医疗方面,心内科疾病数据。12年成立公司,15年7月启动项目。公司占地一间半写字楼,简易大长办公桌“高效办公”,最后一排是运营,然后……没什么可说的。让我想起了曾几何时的公司。
人文方面没什么可谈的,从运营只言片语听出来,公司做的发展方向是“免费推广”,和客户签协议……换句话说,也就是变向把数据拉到自己的平台上。心里一句呵呵……周鸿祎么?但你的公司是个A轮融资的公司哦。你早十年这么干是先进,但到现在看看,你的系统解决不了实际的客户方问题,你运营在你自己的模式下,他的意义呢……
恐怕就是烧投资的钱想做用户量、数据量。然后所谓先有“用户基础”再想其他。怎么变现,我以后再请个运营大牛就好了。
再看看黑板,黑板上写了不少东西……8条都是关于key的,按照语境翻译一下,应该就是关键词的意思了。这个key有权限控制,客户方和PM和负责的工程师都能加。而且key还有等级限制。……好吧,瞬间想到的思路满足这八条的方案就是把用户权限系统挂到Key上。然后Key取的时候异步Redis当缓存,变更的时候再通知,各种各种。再来看所谓的多接口,OK,这东西在医疗行业是个独揽的坑,虽然面试时候和人家说5万一个接口,但我的兄弟们做数据对接接口貌似20万也是有的。
黑板上写了Leader和三个项目组,好吧,PM职位在这家公司可以不考虑了,苦逼一个然后上面还有个管理,在上面是技术总监、人事、产品、实施,都是大爷。结合一下百度资料,公司注册资本111万元,然后全是软资质,没有硬性资质……
接下来谈,其实也就没什么好谈的了。这样的公司情况,这样的没有盈利的项目。我只能把话说的比较过分了。尖锐的问题“贵公司如果有一个行业巨头冲入市场,对市场进行抢占,那么贵公司将会怎么做?”
问题的目的不在他如何做,而是看公司的态度了。对方回答竞争,我肯定会接着往下问,竞争的资本。如果他的资本足够让我信服,我当然可以考虑。如果没有这个资本,哪些是潜在资本呢?从总监的位置上,就是“做好自己等着发工资”,这样的项目没上线就这样,还能指望上线么?
但对方的态度是“这种情况不会出现,如果出现了,那是投资人需要考虑的。投资人会比你更着急。”
我只能说,这样的总监,你会认为底下的人是个什么工作状态呢?
一个良好的团队,需要提供给有能力的人发展平台,也需要提供给混的人,混的平台。但,你带头者都是这样的状况,遇到风险先想到的是保本,先想到的是自己的股权能变现。也许他能成功,但我也只能抱着兼容并包的想法去看这件事了。
说的东西有点过分了,临走的时候,他问我说有没有自己觉得出彩的code segament。让我提供一下,就当作笔试了。我思考了一下,回家整理了一点东西,
发给他的代码是如下一段:
/// <summary>
/// 主VM构造逻辑(DEMO)
/// by 阮清寒
/// 2017年1月20日
/// </summary>
public MainViewModel()
{
/// 粗略讲解一下这段代码
/// 这段代码是手动实现了一个队列,不断从数据层读取数据,然后根据数据插入队列
/// 当队列达到上限时,就等待下一个循环
/// 同时维护一个“任务池”,这个任务池只能存放有限数量线程执行工作。
/// 这里,将队列最后处理比作游乐园玩耍。将任务比作游客。游客排队进入游乐园。
/// 通知端代理(如果解耦的话,这里应该把逻辑抽出去。)
/// 连接代理商
clientSocket.Connect(new IPEndPoint(ip, 1201));
/// 游乐园时钟
CommonTimer = new Timer(e =>
{
/// 当游乐园满了,就不再往游乐园放游客了。
/// 利用return做代码优化,防止IL压入没用的代码。优化方式来自于Resharper
if (TaskList.Count >= MaxCount) return;
/// 循环放游客,直到队列慢
for (int i = 0; i < MaxCount - TaskList.Count; i++)
{
/// 如果游乐园里没有位置了,就别往里进了。
if (TaskQueue.Count <= 0) break;
/// 把游客从队列接出来
var k = TaskQueue.Dequeue();
/// 告诉游客玩完了,让游乐园把自己放出去。由于游乐园在自己的线程里,只能靠游乐园把游客清出去,然后游客就靠垃圾回收期去回收了。
k.ContinueWith(cw => App.Current.Dispatcher.Invoke(() => TaskList.Remove(k)));
/// 放游客进入游乐园
App.Current.Dispatcher.Invoke(() => TaskList.Add(k));
/// 游客开始玩吧!
k.Start();
}
/// 每秒钟执行一次
}, null, 0, 1000);
/// 排队时钟
CommonDBTimer = new Timer(e =>
{
/// 取出检票机
var alllist = new GroupConfig()
.GetModelList()
.Where(
eal => eal.Config_Type == Password.GetMD5Hash(
Visit.ModelToJson(
new PasswordBoxModel()
{
Istrue = true,
GroupID = eal.Config_GroupID,
type = "IsAllwebSession",
dt = eal.Config_Time ?? DateTime.Now,
}
)
)
).ToList();
/// 获取全部的游客
var searchList = bll.GetSearchList();
/// 如果队列里面游客的数量,是现在全部游客的2倍了。
/// 说明至少每个人都玩了一次(抽屉原理)。
/// 因此,这次就不让他们排了,等队伍小点再让他们进来玩。
if (TaskQueue.Count > searchList.Tables?[0]?.Rows.Count * 2 || searchList.Tables?[0]?.Rows.Count <= 0) return;
/// 告知每个游客,怎么玩。
foreach (DataRow item in searchList.Tables?[0]?.Rows)
{
///先让他排上队。
TaskQueue.Enqueue(
/// 玩法是:
new Task(
/// 【创建新的搜索实例】(请无视类库命名……)
/// 创建游乐设施
() => new SearchHelper()
/// 游客可能有很多张票
/// 贵宾票就玩高级的模式
/// 没贵宾票就玩外围的模式
.SearchBaiDu(
alllist.IsdtHaveValue(item["SEK_AddGrop"].ToString()) ?
string.Format("{0} site:{1}", item["SEK_KeyWord"], item["LimitList_Url"]) :
string.Format("{0}", item["SEK_KeyWord"]
))
/// 只让有票面而且不过期的项目列进来。
.Where(item2 => !string.IsNullOrEmpty(item2.Date))
/// 游玩项目列成列表
.ToList()
/// 让游客按照列表以此游玩
.ForEach(item2 =>
{
/// 如果成功游玩了记录给我们,并且通知给代理。
if (bill.Add(new Model(item2)) SendModelToClient(item2, item);
})));
}
/// 5秒照看一次
}, null, 0, 5000);
}
请先看过所有的代码,再阅读下面的文段,如果看不懂,请至少看过所有的注释。
在CSDN上,第一次发表自己的代码,这套游乐园,就是我所谓的任务队列。虽然写成这样子,但实际上这是为了过我自己心中代码段不超过50行的规矩,压缩起来实际上也只是四行代码罢了。注释写的很明白了,当然,有没有更好的做法呢?其实也是有的。因为一个游乐园用时间来控制是不恰当的。当时这个业务访问有超时问题,所以才会用这种写法。利用单位时间内无法完成就自动清人的方式把任务强行回收。
附上访问HTTP代码:
namespace ThomasVMControl.NetConnet
{
using Newtonsoft.Json;
using System.Net;
using System.Text;
using System.Threading.Tasks;
public class Visit
{
public static async Task<string> HttpPost<T>(string Url, T data)
{
var p = new Task<string>(() =>
{
var webReq = (HttpWebRequest)WebRequest.Create(Url);
webReq.ContentType = "application/json";
var dt = string.IsNullOrEmpty(data?.ToString()) ? "" : ((data is string) ? data as string : ModelToJson(data));
webReq.Method = string.IsNullOrEmpty(data?.ToString()) ? "GET" : "POST";
var byteArray = Encoding.UTF8.GetBytes(dt);
webReq.ContentLength = byteArray.Length;
if (!string.IsNullOrEmpty(data?.ToString()))
using (System.IO.Stream newStream = webReq.GetRequestStream())
newStream.Write(byteArray, 0, (int)webReq.ContentLength);
using (var response = (HttpWebResponse)webReq.GetResponse())
using (var sr = new System.IO.StreamReader(response.GetResponseStream(), Encoding.UTF8))
return sr.ReadToEnd();
});
return await p;
}
public static async Task<string> HttpPost(string Url) => await HttpPost(Url, "");
public static T JsonToModel<T>(string json) where T : new() => json != null ? JsonConvert.DeserializeObject<T>(json) : new T();
public static string ModelToJson<T>(T mod) => JsonConvert.SerializeObject(mod);
public static async Task<TB> HttpPost<TA, TB>(string url, TA mod) where TB : new() => JsonToModel<TB>(mod != null ? await HttpPost(url, mod) : await HttpPost(url));
}
}
/// WPF ViewModel基类
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected void RaisePropertyChanged<T>(Expression<Func<T>> action) => RaisePropertyChanged(GetPropertyName(action));
private static string GetPropertyName<T>(Expression<Func<T>> action) => ((MemberExpression)action.Body).Member.Name;
}
像这样的代码,我没有去写注释,因为只是我自己来调用。大致看一下就好……囧rz
写的比较小巧的HTTP访问,实际使用的时候,在外层写了try-catch,然后设置了HTTP访问超时的设定,数据库那边连接也是。
然而……放这段代码出来意义呢?其实只是想说,代码你也许看不懂,但注释我相信认真看的话,会不会写程序的人,基本都看得懂吧。程序就是人的生活经验。有时候真的在想,我们是不是把程序脱离生活太远了。程序这东西,难的不是表象。真正难的是人对生活的理解。