Comencé a programar en Go después de un período bastante largo de programación en PHP. Supongo que, a juzgar por las últimas tendencias, mi caso está lejos de ser aislado. Go, en general, está ganando popularidad entre los desarrolladores web.

Entonces, estoy en el mundo de las ardillas. ¿Y qué hace un programador PHP quemado hasta la médula cuando está allí? Así es, sigue "resoplando" - debido a su deformación profesional - pero ya en Go, con todas las consecuencias consiguientes.
, Go , , SOLID, Dependency Injection . , ( ), , - PHP, ++ Java.
, , . context.Context, ? , PHP . - ! , , PHP , , Go. . , . , proof of concept. Go, .
, , , , : ", , . , ". — . Reddit Go , … . "? ? over-engineering" - . : " . , README.md - ".

, , , .
? - . :
Job - , (task).
-, L4; , backend ; backend , . Job. Github.
Job - Command pattern, . :
Parallel Processing Where the commands are written as tasks to a shared resource and executed by many threads in parallel (possibly on remote machines; this variant is often referred to as the Master/Worker pattern)
, - — . , , context.Context, , . task.Assert
if err != nil { panic(err) }.
ping/pong . , — Github .
// Saves resized image to the output dir
func (s *ImageResizer) SaveResizedImageTask(j job.Job) (job.Init, job.Run, job.Finalize) {
// Do some initialization here
init := func(t job.Task) {
if _, err := os.Stat(s.inputDir); os.IsNotExist(err) {
t.Assert(err)
}
if _, err := os.Stat(s.outputDir); os.IsNotExist(err) {
err := os.Mkdir(s.outputDir, 755)
t.Assert(err)
}
}
run := func(task job.Task) {
stream := j.GetValue().(netmanager.Stream)
select {
case finishedTask := <- j.TaskDoneNotify(): // Wait for the scanner task to be done
if finishedTask.GetIndex() == s.scanneridx {
s.scandone = true
}
task.Tick()
case frame := <-stream.RecvDataFrame(): // Process response from the backend server
task.AssertNotNil(frame)
res := &imgresize.Response{}
err := frame.Decode(res)
task.Assert(err)
baseName := fmt.Sprintf("%s-%dx%d%s",
res.OriginalName, res.ResizedWidth, res.ResizedHeight, res.Typ.ToFileExt())
filename := s.outputDir + string(os.PathSeparator) + baseName
if ! s.dryRun {
ioutil.WriteFile(filename, res.ImgData, 0775)
}
j.Log(1) <- fmt.Sprintf("file %s has been saved", filename)
stream.RecvDataFrameSync() // Tell netmanager.ReadTask that we are done processing the frame
s.recvx++
task.Tick()
default:
switch {
case s.scandone && s.recvx == s.sentx: // Check if all found images were processed
task.FinishJob()
default:
task.Idle() // Do nothing
}
}
}
return init, run, nil
}
Esta es una de las tareas del cliente, que procesa la respuesta entrante del servidor y almacena la imagen resultante. La tarea organiza su ejecución, utilizando la técnica de sincronización ping / pong antes mencionada, con una tarea que escanea archivos. También determina cuándo llegó la última respuesta del servidor y cuándo completar la ejecución de todo el trabajo (Trabajo).
Hasta qué punto esta solución está sobre-diseñada y hasta qué punto su uso en lugar del contexto está justificado - deje que el lector decida, expresé mi opinión en forma de sarcasmo en la imagen de arriba.
Que tengan un gran fin de semana a todos y que el poder de pehape nos acompañe en el mundo de las tuzas.