Как стать автором
Обновить

Комментарии 71

Жаль, что нельзя использовать для написания мобильных приложений.
Смотря для каких! Под ARM кросс-компилируется.
Всё очень сложно, над проблемой работает 1 человек из команды Ubuntu Touch, так что пока только для Ubuntu Touch. С Android дела обстоят хорошо, уже можно создавать приложения на базе нативной активити и рисовать через EGL, но поддержка QML пока отсутствует.
Только что на конференции привели пример: софт для Beacon (PayPal) написан на GO.
А есть что-нибудь а-ля go on rails? :) а то ваш пример ни для чего, что больше хелло ворлд, не подойдет, уж слишком low-level.
Есть Revel, оно или нет не скажу, т.к. Ruby не использовал, а Revel так и не осилил, ибо для моих целей проще, удобнее и быстрее было писать на pure Go. А то что получалось с Revel, выглядело как то, что написано на pure Go, только запутаннее.
Есть Revel или Gobee. Но использование высокоуровневых фреймворков это не go-way. Да и не за чем, почти все есть в комплекте. Если чего не хватает, можно добавить из таких тулкитов как Gorilla. Я предпочитаю использовать Martini. Это не полноценный фреймворк, но несколько упрощает жизнь.
Прошу прощения, второй фреймворк называется beego.
Может быть большие проекты это тоже не go-way тогда? :) Глядя на ваш код,

  1. Нет стандартов
    Хоть язык и не накладывает ограничений на структуру проекта, данное приложение я решил организовать по модели MVC

    Значит каждый будет организовывать код как хочет.
  2. Пакет main это какой-то god-object. Он и сторонние пакеты подключает и route, и headers меняет, и auth делает.
  3. Все ошибки в одном файле, нет translations.
  4. С моделями совсем все плохо.
    а) создаются соединения с базой прямо из методов,
    session := utils.NewDbSession()
    defer session.Close()
    c := session.Col(«posts»)

    б) не знаю есть ли статические методы в Go, но делать так:
    post := models.NewPost()
    err := post.LoadById(id)

    В других языках не принято, да и не очень логично.
    в) Валидации нет, не знаю где она у вас будет, но предчувствую плохое.


Так что даже не знаю как можно написать что-то большое таким образом. Все-таки full-stack фрэймворки существуют не просто так :)
Да, многие решения были приняты как раз исходя из малого размера проекта. Возможно, для более крупных проектов и есть смысл использовать фреймворк. Либо какое-либо внутреннее соглашение о структуре кода и т. п.
По поводу замечаний.
1. Да, язык системный и служит для разных целей. Можно придерживаться best practice или соглашений.
2. Пакеты подключаются в любом файле. Решил использовать роуты и все что к ним относится в main из-за небольшого размера проекта.
3. То же самое, проект небольшой.
4. а) Да, тоже думал над этим. Можно было для контроллеров сдлеать middleware который бы автоматически подключался и отключался от бызы, но хотелось бы чтобы модели были независимы от контроллеров.
По поводу пункта б. Не могли бы пояснить что вы имели ввиду? Создание нового поста отдельной функцией models.NewPost()? Если да, то это принятый в языке способ.
в) Опять же, в силу малого размера проекта, валидация простая. Часть валидации берет на себя структура данных, т.е. вы не сможете сохранить стоку в поле с типом int. Часть критичной валидации (без которой пост не сохранится, например не передали id) проверяется в модели, возвращая ошибку. Остальное, если нужно проверяется в контролере. Но этих проверок мало. С одной стороны кажется что все разбросано по разным местам, но определенная логика в этом есть. Методы модели, например, можно использовать не только из контроллеров, а отдельно внутри (для тех же тестов, например). В более серьезных проектах правила валидации задаются тегами в структуре данных. Несомненно, это более правильный подход.
Основная валидация все-таки сделана у меня в модели. Она возвращает ошибку, которую потом обрабатывает контроллер. Как-то так:

func (p *Post) Create() (id string, err *conf.ApiError) {
	// validation
	switch {
	case p.UserId == "":
		err = conf.ErrUserIdEmpty
	case p.Body == "":
		err = conf.ErrPostBodyEmpty
	case utf8.RuneCountInString(p.Body) > conf.MAX_POST_CHARS:
		err = conf.ErrPostMaxSize
	case p.Geo.Coordinates[0] == 0.0 || p.Geo.Coordinates[1] == 0.0:
		err = conf.ErrPostLocationEmpty
	}
	if err != nil {
		return
	}

	p.Id = bson.NewObjectId()
	p.Geo.Type = "Point"
	p.Enabled = true
	p.Date = time.Now()

	session := utils.NewDbSession()
	defer session.Close()

	c := session.Col("posts")
	errDb := c.Insert(p)

	if errDb != nil {
		return "", conf.NewApiError(errDb)
	}

	p.UpdParentComments()

	return p.Id.Hex(), nil
}
Я бы не сказал, что этот метод хорош, и, думаю, многие согласятся.
  • Обычно валидацию отделяют от модели
  • Метод не должен быть таким большим


По поводу пункта б) — я имею в виду, что вы вызываете post.LoadById(id) у инстанса, обычно это делают у класса, статически, например, Post.find(id). (если это AR)
в) — Все зависит от паттерна для реализации моделей, если это AR, то она должна сама иметь инстанс какого-то адаптера базы или где вы там храните данные. В вашем случае каждая модель имеет свое соединение к базе. А это дублирование. А дублирование — это плохо. Напрашивается наследование или что там есть в Go.

Получается, все ваши аргументы сводятся к тому, что проект небольшой, а зачем тогда эта производительность и прочие плюшки Go, если небольшой проект можно накидать за пару команд на чем угодно и я уверен, что это будет быстрее и надежнее (ввиду того что по стандартам сделано или даже кодогенерацией).
По поводу год-объекта: пакет скорее аналог нэймпэйса, чем класса.
в namespace вы задаете routes, делате Auth и подменяете заголовки?
В Go чаще всего используется не один большой фреймворк, а комбинация нескольких пакетов.

Но как правильно сказали в Braintree, иногда кажется что в Go надо постоянно переделывать простые вещи, поэтому мы создали для этого отдельный проект на github для того, чтобы не повторять рутинные операции, например по декодированию Json из тела ответа, ошибок API, http клиент, валидации и т.д. (он сейчас просто сборка функций, можно сказать в альфа-версии, поэтому ссылку не привожу — можете посмотреть на github, если интересно, по моему имени пользователя на хабре)

Например, Martini очень простой для понимания пакет, особенно удобно его Dependency Injection (правда с ним нужно быть аккуратнее, потому что при компилировании не будут выводится никакие ошибки из-за того, что зависимости передаются динамически — например передан пустой указатель).

В Go шаблоны (templates) по-моему отличные из коробки. Очень быстрые и легкие в использовании.

В Martini очень просто создавать middleware: мы используем их, например, для подготовки структуры логов и для переменных уровня запросов вот так:

func midRequestValues(c martini.Context, req *http.Request) {
	values := &ReqValues{}
	c.Map(values)
} 

Где ReqValues — это struct со значениями глобальных оповещений (alert'ов), пользователя (берется из Redis в отдельном middleware) и т.д.

Так как map передается всегда как указатель, но можно создать несколько простых функций, которые будут заполнять template.FuncMap какими-нибудь функциями-помощниками, как в Rails. Например:

func (s *Sprockets) SetFuncMap(funcMap template.FuncMap) {
	funcMap["asset_path"] = s.AssetPath
}


Тестирование Martini хитрее. Можно сделать простую функцию App(), которая инициализирует все middleware, создает пути и возвращает *martini.Martini.

func App() *martini.Martini {

	m := martini.New()

	// Setup default middleware
	m.Use(martini.Recovery())

	r := martini.NewRouter()

	r.NotFound(func() (int, string) {
		return 404, wutapi.Error404()
	})

	// Add the router action
	m.Action(r.Handle)

	return m
}


После чего в тестах можно вызывать ServeHTTP и смотреть результат. Например:

func Test_Applications_GetAll(t *testing.T) {

	res := httptest.NewRecorder()
	req := makeReq("GET", "/v1/oauth2/applications")
	App().ServeHTTP(res, req)

	assert.Equal(t, res.Code, 200, "they should be equal")
}


Этот подход удобен тем, что почти все пакеты используют стандартную библиотеку (к слову, очень хорошо и грамотно сделаную, можно даже сказать потрясающе, но у меня мало опыта сравнения например с Java или C++ — мне лично стандартная библиотека Go очень понравилась), поэтому можно заменять и дополнять пакеты как душе угодно и как лучше для проекта.

Проблемы взаимосвязи между пакетами решатся на уровне компиляции.

Первый раз только сложновато было придумать структуру проекта и разобратся что и где использовать, а потом это кажется уже натуральным.
Странное заявление. Как раз хелло ворды пишут на модных фреймворках. А когда речь заходит о серьезных крупных проектах, там все фреймворки пишутся под себя, потому что стандартные перестают подходить в тех или иных местах.
Вы серьезно? В мире есть только Twitter, Facebook и ВКонтакте? Вы говорите о super high load проектах, даже не так — SUPER HIGH LOAD. Весь остальной интернет живет на готовых фреймворках (я про web приложения, а не про сайты визитки). Что-то на php(zend-symfony-yii), ruby on rails, django, всякая джавятина для enterprise. И это немаленькие проекты (как вам github на rails?).
Дело вовсе не в моде (может на Go как раз и мода), а в том, что эти инструменты работают и дают большие преимущества в скорости разработки и в наличии сотен маленьких решений для ежедневных проблем (речь как минимум про rails и ruby gems).
Нынче модно форумы на вебсокетах делать. code.google.com/p/go/source/browse/?repo=net#hg%2Fwebsocket — достойная библиотека. Martini и MVC для такой простой задачки на мой взгляд избыточны, net/http из стандартной библиотеки хватило бы. Пост понравился.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Язык создавался с прицелом на веб-разработку
Отчего же? Насколько мне известно, он задуман как язык общего назначения.
Да, совершенно верно. Я просто хотел подчеркнуть что все для веба там есть.
В Go лично мне еще нравится автоформатирование исходного кода с помощью утилиты Gofmt (особенно когда редактор запускает форматирование кода после каждого сохранения файла, например SublimeText с расширением GoSublime).

Так удобно, когда все в одном стиле.
Это не фишка языка, а, скорее, редактора кода. Сейчас любой нормальный редактор умеет форматировать код по нужному стандарту. Причем некоторые умеют все файлы в папке обработать… :)
Эта утилита идёт в составе языка. Даже если вы пишите код в любом блокноте, есть возможность его правильно отформатировать.
Это не фишка языка, а, скорее, редактора кода.

Не совсем. Например, если фигурные скобочки ставить не в египетском стиле, а не следующей строке, то компилятор откажется такой код компилировать :).
Добавлю пару хвалебных песен про Go :).

Из того, что мне понравилось.

1. Можно парсить подмножество xml-данных в статически типизированные определенные пользователем данные! Очень круто. Ну, то есть, вы объявляете иерархический набор структур, с только теми полями, которые вам нужны, и Go в рантайме создаст вам из xml набор этих структур.

2. Создание форматтеров для дат. Вам нужно передать строку в которой будет записана дата «Mon Jan 2 15:04:05 MST 2006» в формате, который вам нужен (например «02.01.06»). По-моему гениально :)
2. Создание форматтеров для дат. Вам нужно передать строку в которой будет записана дата «Mon Jan 2 15:04:05 MST 2006» в формате, который вам нужен (например «02.01.06»). По-моему гениально :)

>>strftime?
Нет, вы вероятно не совсем так поняли. В go не нужно вообще писать/запоминать все эти спецификаторы навроде %h, %m, %d, и т.п., ну и соответственно, ошибиться будет намного сложнее.

golang.org/pkg/time/#pkg-constants
golang.org/pkg/time/#example_Time_Format
golang.org/pkg/time/#example_Parse
Полиморфизм реализуется интерфейсами, которые не нужно явно имплементировать, а лишь достаточно реализовать методы интерфейса.

Заставила призадуматься эта фраза. Каким образом это выглядит?
Посмотрите мой пример с ошибкой ApiError. Я реализую метод .Error(), что автоматически означает, что ApiError соответствует стандартному интерфейсу error. И там, где ожидается error, я могу передать свою ApiError.
Утиная типизация, то бишь.
Да, я это имел ввиду.
Скорость и компиляция. Скорость у Go в десятки раз быстрее, чем у скриптовых языков, при меньшем потреблении памяти. При этом, компиляция практически мгновенна. Весь проект компилируется в один бинарный файл, без зависимостей. Как говорится, «просто добавь воды». И вам не надо заботиться о памяти, есть сборщик мусора.


Ты смотри, прям золотая пуля какая-то.
Скриптовые языки при правильном использовании медленнее нативного кода в 2-5 раз. Выходит, Go в десяток раз быстрее C?
А уж сказки про то, что в языках со сборщиком мусора можно добиться приемлемой скорости, в 2014 году выглядят даже не смешно.
sealedabstract.com/rants/why-mobile-web-apps-are-slow/ (Хабраперевод: habrahabr.ru/post/188580/)
Сборщик мусора накладывает некоторые ограничения на сферы использование языка. В частности, в приложениях реального времени его использовать не стоит.
Go, конечно, не быстрее C (может только чуть-чуть и только в особых случаях). Но в реальных веб-приложениях порядки такие. Скорость генерации веб-страницы будет в разы/десятки быстрее. Но, опять же, смотря с чем сравнивать.
Вот человек переписал с Ruby на Go свой API и получил 50-кратный прирост — My simple web API is 50X faster....
> Вот человек переписал с Ruby на Go свой API и получил 50-кратный прирост

Это говорит исключительно о том, что он очень фигово пишет на Руби.

Сомневаюсь, что человек в должности Sr Software Architect, который написал книгу о Ruby (изд. O’Reilly), активный участник Ruby-сообщества, контрибьютор, и спикер на различных конференциях, «фигово» пишет на Ruby.
Ну значит, он очень фигово написал сам Руби.
Сам создатель Руби ставил своей целью не скорость работы приложений написаных на нем, а скорость и удобство разработки на нём.
Но не в 50 же раз.
Вы вот говорите про преимущества, но преимущества перед чем? Среди каких «всех остальных языков» его можно выделить?

И про преимущества:
Талисман у Go — дурацкий какой-то, само имя — тоже (приходится искать не "{langName}", а "{langName} programming language").

Open source — очень спорное преимущество: да, в любой момент ты можешь форкнуть проект и пойти своим путём, но по факту много ли таких людей будет? А если не форкать, то шанс того, что в проект будут принимать твои изменения багов, какие-то новые фичи и прочее — достаточно маленький.

Про простой синтаксис — это, тоже неоднозначно. Меня, например, убивает первый же пример, предоставленный на сайте:
package main

import "fmt"

func main() {
	fmt.Println("Hello, 世界")
}

Вопрос: почему модуль fmt занимается выводом?
Или вот ещё: почему функция main написана с маленькой буквы, а Println — с большой?
А зачем при импорте имя модуля в кавычках? Там что, можно использовать переменные?

следующий пример новичку будет сложно распарсить:
func NewField(w, h int) *Field {
	s := make([][]bool, h)
	for i := range s {
		s[i] = make([]bool, w)
	}
	return &Field{s: s, w: w, h: h}
}

А эти * и & перед именами переменных напоминают сишечку (так не лучше ли на си и писать?)

Хоть язык и задумывался как замена C/C++, но на него стали переходить люди с Python, Ruby. Прежде всего, с ними и сравниваю. По удобству разработки и порогу входжения они сравнимы, но Go дает дополнительные преимущества.

По поводу поиска языка, советую искать «Golang».

Действительно C многое напоминает. Вот, например, описание пакета fmt:
Package fmt implements formatted I/O with functions analogous
to C's printf and scanf.


По поводу маленькой и большой буквы (main / Println), тут есть объяснение. То, что с большой буквы — экспортируется. См. объяснение в тексте статьи.

По поводу кавычек в импортируемом пакете. Точно не уверен, но возможно это связано с тем, что можно делать алиасы к пакетам. Написать, например, так import myfmt "fmt" и использовать в коде вместо fmt — myfmt.

Приведенный пример с NewField очень простой. Если немного набить руку, все читается просто, там применяются стандартные вещи и сокращения.
Ну в том же Python'е алиас для импортируемого модуля можно сделать через «as» безо всяких кавычек:
import foo as bar


Если бы он просто Си напоминал — ещё пол-беды, но зачем делать присваивания через ":="?
НЛО прилетело и опубликовало эту надпись здесь
:= — это вывод типа, создание переменной в lvalue и само присваивание.
Обычно присваивание через = конечно тоже есть.
Скрытый текст
image
Вы прям как правительство РФ: любое высказанное мнение, противоречащее политике партии — так разжигатель, провокатор и просто нехороший человек :-)
Одно дело — высказывать мнение, другое — выдавать за мнимые недостатки то, чего вы не понимаете.
Я высказываю личное мнение, вызванное своими ощущениями, возникшими на стадии ознакомления с данным продуктом. И для меня эти недостатки не мнимые, а вполне себе реальные.

А про «то, чего я не понимаю» — это вообще странный выпад, опять же, в стиле правительства.
"- они воруют деньги!"
"- да вы ничего понимаете!"
НЛО прилетело и опубликовало эту надпись здесь
Не вижу особой проблемы. Если троллям отвечать неэмоционально и по делу, то для читающих будет польза.
Талисман у Go — дурацкий какой-то, само имя — тоже (приходится искать не "{langName}", а "{langName} programming language").

Нормальный талисман :). Ищется всё хорошо по «golang».

А зачем при импорте имя модуля в кавычках? Там что, можно использовать переменные?

Там можно писать, грубо говоря, урл. Например «github.com/go-martini/martini»

А эти * и & перед именами переменных напоминают сишечку (так не лучше ли на си и писать?)

Не лучше, т.к. <см. преимущества шапке статьи>.
С языком близко не знаком, но вот это порадовало:
import "github.com/go-martini/martini"
и то что оно само вытягивает зависимости пакета при установке
+ аналогичная структура папок в src
Лично мне не очень. Если либа переедет с гитхаба на битбакет, то что, исходники что ли править?
Было бы интересно узнать у реально практикующих на Go — зависимости локально держите, или таки используете go get?
НЛО прилетело и опубликовало эту надпись здесь
Мне не очень нравится, что мне нужно указывать непосредственно в коде, откуда брать библиотеку.
Ну и повторюсь:
Если либа переедет с гитхаба на битбакет, то что, исходники что ли править?

Или я не так понял про go get и можно как-то сделать так, чтобы например при импорте «github.com/go-martini/martini» не писать «github.com/go-martini», а куда-то этот путь отдельно вынести?

За gopkg.in спасибо.
В коде вы указываете, где у вас локально лежит пакет. go get github.com/go-martini/martini скачивает библиотеку в $GOPATH/src/github.com/go-martini/martini.
Но если библиотека меняет хост (или юзернейм на гитхабе например — что кстати уже было с Martini), то мне нужно править все исходники, где сделан импорт «github.com/go-martini/martini» (чтобы go get продолжал работать), так?
Ага.
Но, тут же только import поправить, довольно тривиальная задача. Весь код лопатить не придется.
Локально.
go get делает локальную копию. Создается инфраструктура. И, если качество библиотек устраивает их больше не дергают. Пока поставщик не выкатит что-то в самом деле важное. По крайней мере, все кого я знаю, придерживаются подобной политики.

Если источник мигрирует — просто переписывается путь. Ну а если библиотека исчезает из сети совсем — остается локальная копия.
Есть тонкость, вендоринг. Если вы свой проект опенсорсите для широкой публики, то у импортера вашего пакета зависимости будут собираться с последними коммитами из репозитариев, а не с вашими локальными копиями. Поэтому надо или время от времени пересобирать с go get -u (-u флаг обновляет зависимости) дабы убедиться что ваша поделка работает с последними коммитами зависимостей, или форкать и вендорить зависимости.
НЛО прилетело и опубликовало эту надпись здесь

есть специальная папка vendor

Недавно написал для себя приложение на Go (минималистичный веб-аналог Dash с fuzzy-поиском на биграммах, всего 1000 LOC), ощутил на себе и прелести и недостатки языка.

Из удобств — прозрачный механизм FFI с сишными библиотеками, весьма удобная и функциональная стандартная библиотека, приемлимая скорость работы, неплохие инструменты для профилирования (pprof tool очень помог), удобная модель асинхронной обработки данных.

Недостатков тоже хватает: много дублирования кода при реализации простых структур данных (скучные монотонные Len Swap Less Pop Push), серьёзные потери производительности на вызове через интерфейсы (ручной инлайнинг кода из container/heap увеличил производительность поиска в 5-6 раз), сложности с пакетированием (не так просто собрать deb пакет для Go-приложения, хорошо хоть у Ubuntu есть примеры).
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории