Pull to refresh

Comments 26

А как же тернарные операции и лямбды?


Было бы здорово описать почему именно нет того или иного в Го.

Наследование, конструкторы и деструкторы из мира ООП. Го не ООП язык! О чем статья? О сравнении ООП языков с не ООП языками? Дженерики будут в Go 2. Остальное — усложнение языка, что протеворечит философии Го… Статья ни о чем!

Наследование, конструкторы и деструкторы из мира ООП.

Внезапно всё это не нужно для полноценной реализации ООП. Такие дела.


Го не ООП язык!
Объекты есть, полиморфизм есть, интерфейсы есть, чего это Go вдруг не ООП язык?

Прикольно, на странице по ссылке, которую вы привели, написано, что Go — объектно ориентированный язык. Вы почитали мануал и поняли, что ошибались?

Нет, я не ошибаюсь. Вы, возможно, плохо знакомы с Go, и Вам кажется, что Go ООП-язык, он похож на него и можно написать программу как-будто на ООП-языке, но это не так…
В Go нет абстракции, явной инкапсуляции (здесь это лишь синтаксический сахар) и наследования (есть внедрение структур, не надо путать с наследованием).
Почитайте повнимательнее.

Нет, я не ошибаюсь.

Но сослались на текст, в котором написано, что Go — ООП язык ))


Вы, возможно, плохо знакомы с Go, и Вам кажется, что Go ООП-язык

Наверное авторы FAQ к Go тоже плохо знакомы с Go, потому что им кажется, что Go — ООП язык


В Go нет абстракции

О как. А интерфейсы, это не абстракция, это сплошь конкретика, да?


явной инкапсуляции

То есть можно у параметров, у которых объявлен интерфейс безо всяких танцев с бубнами получить доступ к полям реализации, которых в интерфейсе нет? Прямо как в питоне?


и [нет] наследования

Которое в ООП не обязательно

Но сослались на текст, в котором написано, что Go — ООП язык ))

Там это НЕ написано.


получить доступ к полям реализации, которых в интерфейсе нет? Прямо как в питоне?

Это не связяно с инкапусляцией.


Которое в ООП не обязательно

А что обязательно?

Там это НЕ написано.

Я процитирую.


Is Go an object-oriented language?
Yes and no.

Что на русский переводится следующим образом.
Go объектно-ориентированный язык?
И да и нет.


И далее по тексту пишут, что Go поддерживает ООП, но только немного не так, как в Java и C++.


Это не связяно с инкапусляцией.

Прикольно. А я думал, что инкапсуляция это такая штука, которая позволяет давать доступ к объекту только через интерфейсы и не давать дёргать его поля.


А что обязательно?

Полиморфизм обязательно.

"И да и нет" != "написано, что Go — ООП язык". Это скорее "а фиг его знает", или даже "ой… ну если придумать свое определение ООП..." :)


А я думал, что инкапсуляция это такая штука, которая позволяет давать доступ к объекту только через интерфейсы и не давать дёргать его поля.

Неа, это упаковка данных и кода в одной сущности.


Полиморфизм обязательно.

А почему, вы пользуетесь каким-то конкретным определением ООП?

"И да и нет" != "написано, что Go — ООП язык".

Был бы не ООП, так бы и написали, что не ООП


Инкапсуляция это упаковка данных и кода в одной сущности

И в Go этого нет? И, главное, когда говорят про ООП под инкапсуляцией имеют в виду именно это?


А почему, вы пользуетесь каким-то конкретным определением ООП?

Нет, конкретным определением не пользуюсь, пользуюсь общепринятым мнением. Вон в джаваскрипте инкапсуляции нет, наследования нет а ООП есть. Это потому что там таки есть полиморфизм.

Был бы не ООП, так бы и написали, что не ООП

Я поправил, бгагодарностей не надо.


И в Go этого нет?

Я этого не утверждал. С моей точки зрения, это как раз тут имеется.


И, главное, когда говорят про ООП под инкапсуляцией имеют в виду именно это?

Да. Именно это и имеют в виду — объединение кода и данных. Иные трактовки идут от безграмотности.


Вон в джаваскрипте инкапсуляции нет, наследования нет а ООП есть. Это потому что там таки есть полиморфизм.

Ну разумеется инкапсуляция в джаваскрипте есть.
Наследование там тоже есть, даже ключевое слово extends завезли :D

Я этого не утверждал. С моей точки зрения, это как раз тут имеется.

Итого, инкапсуляция есть, полиморфизм есть, почему тогда Go это не объектно ориентированный язык?


Ну разумеется инкапсуляция в джаваскрипте есть.

Если под инкапсуляцией понимать то, что говорите вы, то есть. А вот extends появилось сравнительно недавно, но ООП в джаваскрипте было и до этого.

Итого, инкапсуляция есть, полиморфизм есть, почему тогда Go это не объектно ориентированный язык?

Наследование забыли =)


На самом деле полиморфизм есть во всех более-менее высокоуровневых языках. Аналогично с инкапсуляцией. Я уже приводил ссылку на статью на вики — есть пример инкапсуляции на C.


Так что если следовать вот такому "новому" определению ООП (где вместо 3 принципов оставили только 2), то Go выходит объектным. Но тогда получится, например, что и Haskell тоже ООП-язык… Да блин, тогда не-ООП языки вообще останутся? :))


А вот extends появилось сравнительно недавно, но ООП в джаваскрипте было и до этого.

Ну так и наследование ведь было, называлось prototypal inheritance.

Наследование забыли =)

И в Go наследования нет вообще и никак нельзя сделать так, чтобы в одном классе можно было воспользоваться методами, которые объявлены для другого?


Так что если следовать вот такому "новому" определению ООП (где вместо 3 принципов оставили только 2), то Go выходит объектным.

То есть, если бы в Go было бы какое-то наследование, то Go был бы объектно-ориентированным даже по вашему "старому" определению?


Но тогда получится, например, что и Haskell тоже ООП-язык…

В Haskell есть полиморфизм подтипов? Если есть, тогда да он вполне себе ООП.


Да блин, тогда не-ООП языки вообще останутся? :))

Может быть Lisp? Fortran там какой-нибудь? А вообще ООП сейчас проникло повсюду, очень удобный концепт получился.


Ну так и наследование ведь было, называлось prototypal inheritance.

И код, в котором не использовалось наследование не был объектно ориентированным кодом?

И в Go наследования нет вообще и никак нельзя сделать так, чтобы в одном классе можно было воспользоваться методами, которые объявлены для другого?

Воспользоваться можно, переопределить нельзя.


если бы в Go было бы какое-то наследование, то Go был бы объектно-ориентированным даже по вашему "старому" определению?

Ну да. И стал бы при этом хуже, нежели сейчас.


А вообще ООП сейчас проникло повсюду, очень удобный концепт получился.

Ага, настолько удобный, что дизайнеры языков от него совершенно сознательно отказываются (дада, я про Go или, скажем, Rust). Но дабы не потерять популярность "у народа" вынуждены писать, "ой… ну да и нет...". А то иначе плебс не примет.


И код, в котором не использовалось наследование не был объектно ориентированным кодом?

Может быть. На JS вполне можно писать в процедурном стиле.

Воспользоваться [отнаследованным методом] можно, переопределить нельзя.

Итого, получается, что Go не ООП язык потому, что в нем нельзя применить паттерн Template Method. Ну и ладно, я его всё равно никогда не любил!


Ну да. И [если бы в Go было наследование] стал бы при этом хуже, нежели сейчас.

И так как от того, чтобы быть ООП языком Go отделяет возможность переопределить метод, получается, что именно эта возможность ухудшила бы язык. Ну, то есть Template Method это выходит антипаттерн, а не паттерн вовсе.


Может быть. На JS вполне можно писать в процедурном стиле.

Ну вот этот код он объектно ориентированный?


var SquareShape = (function(height){
    var that = {
      area : function () {
         return height * height;
      }
    };
    return that;
});
var RectShape = (function(width, height){
    var that = {
      area : function () {
         return height * width;
      }
    };
    return that;
});

var ComplexShape = (function(shapes) {
   var that = {
      area : function () {
        return shapes.reduce(function(total, curr){
          return total + curr.area();
        }, 0);
      }
    };
    return that;
})

var square = SquareShape(10);
var rect = RectShape(10, 20);
var complexShape = ComplexShape([square, rect]);
console.log(complexShape.area());
И так как от того, чтобы быть ООП языком Go отделяет возможность переопределить метод, получается, что именно эта возможность ухудшила бы язык. Ну, то есть Template Method это выходит антипаттерн, а не паттерн вовсе.

Вы неверно истолковали мою мысль. Решение многих задач через наследование может быть проще (быстрее и легче запилить), но дороже в плане поддержки в долговременной перспективе. Особенно если у вас большой проект, и много "вчерашних студентов" и просто не очень сильных программистов. Читайте "проще натворить фигни".


Ну вот этот код он объектно ориентированный?

В целом да (хотя пример очевидно совсем уж искуственный, и наследования в конкретно этом куске нету, ибо оно тут не нужно). Попробуйте теперь в качестве упражнения добавить сюда метод "вернуть удвоенную площадь". А потом, в качестве усложненного упражнения, добавить метод "вернуть корень из площади" и подумать, как все это можно оптимизировать/улучшить.

Вы неверно истолковали мою мысль. Решение многих задач через наследование может быть проще (быстрее и легче запилить), но дороже в плане поддержки в долговременной перспективе.

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


В целом да (хотя пример очевидно совсем уж искуственный, и наследования в конкретно этом куске нету, ибо оно тут не нужно).

То есть код, который написан на Go и не использует наследование, потому что оно не нужно — в целом объектно ориентированный?


Попробуйте теперь в качестве упражнения добавить сюда метод "вернуть удвоенную площадь".

Я не понял в чём сложность вернуть корень из площади, а код вот, пожалуйста )).


Код под спойлером
var SquareShape = (function(height){
    var that = {
      area : function () {
         return height * height;
      }
    };
    return that;
});
var RectShape = (function(width, height){
    var that = {
      area : function () {
         return height * width;
      }
    };
    return that;
});

var ComplexShape = (function(shapes) {
   var that = {
      area : function () {
        return shapes.reduce(function(total, curr){
          return total + curr.area();
        }, 0);
      }
    };
    return that;
});

var ExtendedShape = (function(shape) {
  var that = {
      area : function () {
        return shape.area();
      },
      halvedArea : function() {
        return shape.area() / 2;
      },
      areaSqrt : function() {
        return Math.sqrt(shape.area());
      }
    };
    return that;
});

var square = SquareShape(10);
var rect = RectShape(10, 20);
var complexShape = ComplexShape([square, rect]);
var extendedRect = ExtendedShape(square);
console.log(complexShape.area());
console.log(extendedShape.halvedArea());
console.log(extendedShape.areaSqrt());
Ну мы с вами выяснили, что от поддержки нормального наследования Go отделяет невозможность переопределить метод.

Нет. Это вы себя для себя придумали. В Go есть композиция с автомагической делегацией. Невозможность переопределить метод — наиболее очевидное, "механистическое" отличие.


В целом да (хотя пример очевидно совсем уж искуственный, и наследования в конкретно этом куске нету, ибо оно тут не нужно).

Я для вас выделил кусочек текста, который вы не заметили.


Я не понял в чём сложность вернуть корень из площади, а код вот, пожалуйста )).

Хорошо, а теперь для тренировки основ:


  • сделайте так, чтобы у самих сущностей *Shape были методы halvedArea и т.п. Ибо сейчас ваш код по сути эквивалентен такому;
  • (задание со *) сделайте так, чтобы для SquareShape не происходило бесполезного возведения в квадрат а затем извлечения корня. Ибо это а) медленно, б) теряется точность.
Нет. Это вы себя для себя придумали.

Нет, это не я придумал, это вы написали, что воспользоваться методом из предка можно, только переопределить его нельзя. И собственно это было единственное отличие, на которое вы обратили внимание.


Я для вас выделил кусочек текста, который вы не заметили.

Вы написали, что код объектно ориентированный, а наследования нет, потому что оно тут не нужно. Потом я спросил, будет ли объектно ориентированным код, написанный на Go, если там наследование будет не нужно. Вы, к сожалению, ничего не ответили.


Ибо сейчас ваш код по сути эквивалентен такому;

Нет, мой код по сути эквивалентен коду, в котором методы есть у самих сущностей, потому, что если вы напишете код, который будет рассчитан на наличие методов у самих сущностей, а я отдам туда ExtendedShape — ваш код разницы не заметит.


сделайте так, чтобы для SquareShape не происходило бесполезного возведения в квадрат а затем извлечения корня.

Вот со звёздочкой
var SquareShape = (function(height){
    var area = height * height;
    var that = {
      area : function () {
         return area;
      },
      halvedArea : function() {
        return area / 2;
      },
      areaSqrt : function() {
        return height;
      }
    };
    return that;
});
Потом я спросил, будет ли объектно ориентированным код, написанный на Go, если там наследование будет не нужно.

Окей. Отвечу. От может быть как объктно-ориентированным, так и нет. Как, например, может быть объектно-ориентирпованным код на С. Целые объектные фрейморки на С есть. Что, однако, не делает этот язык ООП.


По поводу вашего упражнения — плохо. Вы пишете про "код, который рассчитан на наличие методов у самих сущностей", но ваши сущности эти методы не имеют.
Вызов RectShape(1).areaSqrt() вернет что? Бросит ошибку.


Ну а ваше решение упражнения со звездочкой — типичное гоферовское (это не комлимент). Дабы оптимизировать один метод (areaSqrt) что сделали? Правильно, скопипастили все методы из другой сущности и переписали один из них, так что у нас есть сейчас две реализации 'halvedArea'. Короче опять незачет.

Целые объектные фрейморки на С есть. Что, однако, не делает этот язык ООП.

Получается, что Go от того, чтобы стать ООП языком отделяет возможность переопределить метод у потомка. С таким же успехом можно сказать, что Java не ООП язык, потому что в нём нет множественного наследования.


ваши сущности эти методы не имеют. Вызов RectShape(1).areaSqrt() вернет что? Бросит ошибку.

Да нормально всё там. Просто делать объекты надо вот так: ExtendedShape(RectShape(1)) и тогда в них будут нужные методы.


Дабы оптимизировать один метод (areaSqrt) что сделали? Правильно, скопипастили все методы из другой сущности и переписали один из них, так что у нас есть сейчас две реализации 'halvedArea'. Короче опять незачет.

В требования, которые вы озвучили решение вписалось, что касается копипасты, которой кстати в решении нет — у всех свои недостатки )). Но если бы копипаста была, и нужно было бы её убрать, то вот так можно было бы это сделать, я на ёё наличии не настаиваю.


Решение без копипасты
function halfArea(area) {
    return area / 2;
}

var SquareShape = (function(height){
    var area = height * height;
    var that = {
      area : function () {
         return area;
      },
      halvedArea : function() {
        return halfArea(area);
      },
      areaSqrt : function() {
        return height;
      }
    };
    return that;
});

var ExtendedShape = (function(shape) {
  var that = {
      area : function () {
        return shape.area();
      },
      halvedArea : function() {
        return halfArea(shape.area());
      },
      areaSqrt : function() {
        return Math.sqrt(shape.area());
      }
    };
    return that;
});

Кстати, вы тут говорили, что решение многих задач через наследование дороже в плане поддержки в долговременной перспективе. Решите, пожалуйста, упражнение без применения наследования, не важно на каком языке, покажите класс! Мне интересно посмотреть что вы имеете в виду под решением, которое дешевле в долгосрочной перспективе.

Да нормально всё там.

Ну коли так, то бесцельно спорить с вами я вижу смысла ровно ноль :)


Решите, пожалуйста, упражнение без применения наследования, не важно на каком языке, покажите класс!

Суть таких задач ведь не написать идеальное решение (пример все равно стерильно искусственный). Суть — на своем личном опыте понять те или иные концепции, в данном случае самому придти к пониманию, что либо мы используем наследование, либо отходим от ООП. Вы выбрали вариант два, перейдя к процедурному стилю (вынесли общий код во внешнюю процедуру, забив на инкапсуляцию кстати).


А показывать код, объяснять что и для чего, почему оно так а не иначе, разжевывать — абсолютно нецелесообразно. Это как бисер перед свиньями метать — время убъешь, бисер потратишь, а свинья останется свиньей.

Ну коли так, то бесцельно спорить с вами я вижу смысла ровно ноль :)

Как-то странно выходит. Вы попросили сделать так, чтобы методы были в сущностях. Я вам ответил, что они уже там.


Вы уточнили, что RectShape(1).areaSqrt() вернёт ошибку.


Я написал, что сущности надо изготавливать вот так ExtendedShape(RectShape(1)) и тогда всё будет нормально.


И после этого вы пишите, что смысла спорить со мной — ноль. Методы в сущностях, всё сделано как вы просили, по этому поводу нам спорить действительно не о чем, но вас похоже не устраивает, что для создания объекта нужно обернуть конструктор прямоугольника. Мне странно что вас это смутило, ведь в объектах метод есть, но можно сделать конструктор, который не надо оборачивать, это не то чтобы проблема.


Суть таких задач ведь не написать идеальное решение (пример все равно стерильно искусственный).

Я не прошу идеального решения, я прошу продемонстрировать вашу точку зрения. Посмотреть на решение без наследования, которое лучше в долгосрочной поддержке.


в данном случае самому придти к пониманию, что либо мы используем наследование

Ну тут вы сформулировали задачу так, чтобы подтолкнуть к использованию не просто наследования, а конкретного паттерна Template Method.


либо отходим от ООП

Или можно, как я продемонстрировал, применить композицию и не отходить от ООП.


Вы выбрали вариант два, перейдя к процедурному стилю

Мне хотелось коротко продемонстрировать идею, и я считаю, что вынос алгоритма в статический метод концептуально ничего не меняет, покуда клиентский код получает объекты, которые может использовать без необходимости знать, как они устроены изнутри. Но если вас смущает богомерзкий статический метод, его всегда можно заменить православным DI. Вот, посмотрите.


Код с поправленным конструктором и DI
var halfArea = function(area) {
    return area / 2;
};

var PureRectShape = (function(width, height){
    var that = {
      area : function () {
         return height * width;
      }
    };
    return that;
});

var ExtendedShape = (function(halfArea) {
  return function(shape) {
    var that = {
        area : function () {
          return shape.area();
        },
        halvedArea : function() {
          return halfArea(shape.area());
        },
        areaSqrt : function() {
          return Math.sqrt(shape.area());
        }
      };
      return that;
    }
}(halfArea));

var RectShape = (function(PureRect, ExtendedShape) {
  return function(width, height) {
    return ExtendedShape(PureRect(width, height))
  };
} (PureRectShape, ExtendedShape));

var SquareShape = (function(halfArea) {
  return function(height){
    var area = height * height;
    var that = {
      area : function () {
         return area;
      },
      halvedArea : function() {
        return halfArea(area);
      },
      areaSqrt : function() {
        return height;
      }
    };
    return that;
  }
}(halfArea));

var ComplexShape = (function(shapes) {
   var that = {
      area : function () {
        return shapes.reduce(function(total, curr){
          return total + curr.area();
        }, 0);
      },
      halvedArea : function () {
        return shapes.reduce(function(total, curr){
          return total + curr.halvedArea();
        }, 0);
      },
      areaSqrt : function () {
        return shapes.reduce(function(total, curr){
          return total + curr.areaSqrt();
        }, 0);
      }
    };
    return that;
});

var square = SquareShape(10);
var rect = RectShape(10, 20);
var complexShape = ComplexShape([square, rect]);

console.log(square.area());
console.log(square.halvedArea());
console.log(square.areaSqrt());

console.log(rect.area());
console.log(rect.halvedArea());
console.log(rect.areaSqrt());

console.log(complexShape.area());
console.log(complexShape.halvedArea());
console.log(complexShape.areaSqrt());

А показывать код, объяснять что и для чего, почему оно так а не иначе, разжевывать — абсолютно нецелесообразно.

Пожалуйста, сделайте ради меня исключение. Думаю, что много объяснять и разжёвывать не придётся, скорее всего демонстрации кода будет достаточно.

UFO just landed and posted this here
Sign up to leave a comment.