All streams
Search
Write a publication
Pull to refresh
-1
0.4
Send message

Смоделируйте, что понадобилось добавить печать в rtf. И в одной конфигурации приложения надо вызывать один метод, а в другой - другой? А если в разные моменты разные методы? Сколько файлов пришлось перетрясти? А если один разраб будет реализовывать для rtf, а другой для html?

Честно говоря, эти шаблоны проектирования - это о том, как извернуться и решить проблемы с недостатками ООП в общем и языка в частности.

Я вам больше скажу. В современных практиках максимально отходят от наследования и заменяют их композицией. SOLID и Spring - именно про это. Бины в Spring - это, по сути, ссылки на модули в терминологии ПП, в которых определены методы для обработки объектов определенных Record-ов.

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

Так думаю:

//файл user.pas
record User 
begin
...
end;

//файл formatter.pas
interface Formatter 
begin
  format(user: User);
end;

//файл DisplayFormatter.pas
module DisplayFormatter: Formatter 
begin
  format(user: User)
  begin
    //печатаем
  end;
end;

//файл SomeClass.pas
module SomeClass
var formatter: Formatter; //Здесь система должна сама подставить
begin
  someMethod()
  var 
    user: User;
    fullName: String;
  begin
    user := ... откуда-то берем
    fullName := formatter.format(user);
    ... дальше используем fullName
  end;
end;

Вот такая система была бы просто бомба.

Да. Инстанс DisplayFormatter создается один раз и затем инжектится во все инстансы классов, которые его используют. В DisplayFormatter нельзя добавлять поля, которые используются в бизнес-логике, только инфраструктурную информацию, типа подключения к базе данных, логер и настройки из yml (кстати, как вы будете хранить эту информацию в ваших чистых функциях?)

Самое главное, как вы будете определять, какую функцию передавать в другую функцию в случае, если например, для одних настроек приложения надо использовать DisplayFormatter, а для других - RTFFormatter? В Spring и вообще при применении DI фреймворк создает нужный инстанс и инжектит его в классы потребителей (в SomeClass) - в поле с интерфейсом Formatter присваивает нужный инстанс.

В Java даже специально ввели record для этого

public record ИмяRecord(Тип1 поле1, Тип2 поле2, ...) {
    // Дополнительные методы или конструкторы (если нужно)
}

Интерфейс Formatter работает только с User. Что значит "Переиспользовать для собаки" - вы угораете? Formatter работатает ТОЛЬКО с User - в этом и заключается контракт. User должен печататься ровно одним способом, как определит бизнес-аналитик (не вам решать). Собака печатается совершенно другим способом (учитывая еще развилки для WebFormatter и RTFFormatter).

Что значит "получается что Formatter зачем то требует целиком тип User со всем что у него есть, используя всего лишь firstName и lastName?"? User - это цельный законченный класс, он передается через указатель любому классу и методу, который его обрабатывает, без потрошения и изменения. Вы каждый раз распаковываете поля и передаете методу, который что-то делает с информацией из User? Зачем?

Самое главное - если потребуется поменять набор полей, добавить, например "Обращение", ("Сэр", "Леди") в User и изменить метод печати, то в моем примере, меняется только классы User, и реализации format (Интерфейсы не меняются), и SomeClass тоже не меняется. А в вашем случае придется менять ВСЕ вызовы в приложении, а их тысячи может быть. То есть, детали реализации User и печати проникают в тысячи других классов всего приложения. Не надо так.

Вообще, я не понимаю, что вы понимаете под словом "Переиспользовать"?

Да пожалуйста:

Вот ваш код:

class User {
  firstName: string
  lastName?: string
  middleName?: string
  ... // Другие поля, не нужные для getDisplayName.

  constructor(firstName: string, lastName?: string, middleName?: string) {
    this.firstName = firstName
    this.lastName = lastName
    this.middleName = middleName
  }

  // Метод.
  getDisplayName() {
    return [this.firstName, this.middleName, this.lastName]
      .filter(Boolean)
      .join(" ")
  }
  
  ... // Другие методы, не нужные для getDisplayName.
}

// Функция.
const getDisplayName = (user: {firstName: string, lastName?: string, middleName?: string} | null | undefined) => {
  if (!user) return undefined

  return [user.firstName, user.middleName, user.lastName]
    .filter(Boolean)
    .join(" ")
}

// Еще более гибко, но может быть менее удобно.
const getDisplayName = (firstName: string, lastName?: string, middleName?: string) => {
  ...
}

Вы критикуете ООП то, что в класс добавляются без конца методы, "приколочены", необходимость class extension, как костыль в шарпе и показываете, что типа функция - это круто. Отделение функциональности от данных.

Ваши придирки давным-давно разобраны Дядей Бобом. Знаете, кто это? Так узнайте, почитайте его книги. И более того, реализовано в Spring и прописано в SOLID.

Для решения вашей проблемы делается следующее.

Создается класс User

public class User {
  private String firstName;
  private String lastName;

  ... Здесь конструктор
  ... Здесь геттеры прямо либо через ломбок
}

Далее создаем отдельный класс, который будет заниматься только одной задачей - формированием информации для вывода на дисплей.

public class DisplayFormatter implements Formatter {
  public String format(User user) {
    return user.getFirstName + " " + user.getLastName;
  }

  public String format2(User user) {
    return "Имя: " + user.getFirstName + ", Фамилия: " + user.getLastName;
  }
}

До кучи и при необходимости добавляем классы WebFormatter, RtfFormatter и т.д.

далее используем:

publi class SomeClass {
  private Formatter formatter = ... здесь dependency injection 
   любым доступным вам способом
    
  public SomeMethod() {
     User user = ...
     String result = formatter.format(user);
  }
}

Вот ЭТО - - гибкость (благодаря DI).

В результате набивается в дальнейшем функционал без разбухания и изменения User, что вам и хочется иметь.

А также ObjectPascal и последовавший за ним Delphi. А вдохновителем - Simula и SmalTalk.

Сериалазация и копирование:

import com.fasterxml.jackson.databind.ObjectMapper; // Для работы с JSON
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) throws Exception {
        // Создаем объект user
        List<String> friendIds = new ArrayList<>();
        friendIds.add("2");
        User user = new User("1", "Вася", "Петров", friendIds);

        // Поверхностное копирование с изменением и добавлением полей
        User updatedUser = new User(user.getId(), "Василий", user.getLastName(), user.getFriendIds());
        updatedUser.setMiddleName("Иванович"); // Добавляем опциональное поле

        // Глубокое копирование (используем сериализацию/десериализацию)
        ObjectMapper objectMapper = new ObjectMapper();
        String userJson = objectMapper.writeValueAsString(user); // Сериализация в JSON
        User clonedUser = objectMapper.readValue(userJson, User.class); // Десериализация из JSON

        // Сериализация в JSON
        String userJsonString = objectMapper.writeValueAsString(user);

        // Десериализация из JSON
        User parsedUser = objectMapper.readValue(userJsonString, User.class);

        // Вывод результатов
        System.out.println("Original User: " + user);
        System.out.println("Updated User: " + updatedUser);
        System.out.println("Cloned User: " + clonedUser);
        System.out.println("User JSON: " + userJsonString);
        System.out.println("Parsed User: " + parsedUser);
    }
}

Чем ваш код лучше этого, что ФП дает в данном примере перед ООП (Сгенерировано DeepSeek)? Функционал отделен от данных (Spring - одно большое отделение).

Вы придумываете какие-то умственные выверты, которые может "решать" ФП и не может, или с трудом - ООП, и при этом пытаетесь доказать, что язык с ООП - сложный и непонятный. Я плохо знаю ФП, и честно говоря, ни разу в своей карьере не встречал случая, когда вот прям необходимо было использовать функциональное программирование. Даже использование лямбд, когда НЕОБХОДИМО было их использовать, был всего один случай. Остальная рутина, когда постоянно используются лямбды, стримы и прочие опционалы - совершенно не являлось необходимостью, просто дань моде и признак крутизны. Да и корпоративная повестка, чо уж.

Ни одного примера на ФП, который вы привели, я не понял. Как и не понял, какие они задачи решают, в чем проблема сделать на ООП. Верю вам на слово. Разбираться, честно говоря, неохота. Когнитивная нагрузка в функциональном программировании и в этом тайпскрипте, на порядок превышает нагрузку обычного программирования на Java.

И ответственность повесили на командира экипажа.

Знаете почему Python вверху списка? Потому что на нем программируют непрограммисты - раз, а во-вторых, из-за того, что у него просто море библиотек, которые все выучить просто невозможно, меняются они часто, а язык (на самом деле) сложный, то программирование сводится к работе в паре с googl-ом. Постоянно гуглишь типа "Python pandas нормализовать колонку". Сейчас я стал постоянно использовать яндекс-нейро и deepsek, и думаю скоро "популярность" нынешних "ведущих" языков программирования внезапно станет падать.

"советские солдаты почему-то охотно сдавались в плен на первых порах " - а я о чем говорю? Зло проявляется в делах. Когда увидите первых расстрелянных, сожженных и замученных - сразу поймете, где зло, а где добро.

"По делам (плодам) их узнаете их". https://www.bible.com/ru/bible/167/MAT.7.15-20.%2525D0%2525A1%2525D0%252598%2525D0%25259D%2525D0%25259E%2525D0%252594

Что там видеть? Когда фашисткая Германия вторглась на территорию Советского Союза и начала убивать наших граждан, жечь деревни десятками, расстреливать в Бабьем Яру (это на Украине) евреев тысячами и угонять людей в рабство и в концлагеря - это что?

Как наши люди должны были реагировать на это? Не сопротивляться? "Насилие это зло?". Ну посмотрите для начала фильм "Судьба человека", может по-другому начнете относиться к идее несопротивления злу.

Когда вашего отца и брата убьют, а мать и сестру изнасилуют и угонят в Германию - вы сразу поймете, что такое зло и что надо делать.

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

Я ведь не просто так написал "Хорошие игры всегда грамотно дифференцируют, где добро, а где зло."

Достаточно просто поставить точку на итерации, и полюбоваться на стек.

Если вы с восьмерки (это сколько лет уже прошло?) насилуете себя и пытаетесь полюбить стримы и использовать их везде, где можно использовать for, то может, вы что-то делаете не так, и стримы не так уж хороши, лаконичны, читаемы и вообще хороши? Может, стоит не только научиться их использовать, но и научиться их НЕ использовать?

Девяносто процентов кода с коллекциями - пройтись по коллекции без ее изменения и получения новой коллекции, просто выполнить ОДНУ какую-то операцию. Может, в этом случае просто использовать for?

Я так понимаю, эти игры подталкивают к идее несопротивления злу?

Любая игра (начиная с мальчишечьих игр) основывается на том, что ребенок ставит себя на место добра и убивает зло. Хорошие игры всегда грамотно дифференцируют, где добро, а где зло.

Насилие - единственный способ остановить зло. Not exception. Любой народ, неспособный воспитать граждан, умеющих, могущих и готовых уничтожать зло, неизбежно вымирает. Пацифизм неплохо продвигать среди своих потенциальных врагов, но не в своих школах.

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

Information

Rating
2,140-th
Registered
Activity

Specialization

Specialist
Java
Oracle
SQL
Git
Spring Boot
Apache Maven
REST
Database