Обновить
4
0
Александр Данилов@gen1lee

13 лет опыта коммерческой разработки

Отправить сообщение

Становиться тем кто принимает решения, аргументированно спорить.

Ответ почему они это делают есть в статье https://habr.com/ru/articles/885980

Это ждёт все ООП языки. Как говорится, «будет хуже» (с).

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

Он занимает ровно те же 78 строк

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

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

Вы согласились написать полный пример для демонстрации вашего процедурного подхода? Согласились.

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

Ну попробуйте заменить swEncoder.requestKeyFrame() на вызов процедуры. У вас это не получится. В этом был весь смысл примера с кодеками.

Я ее заменил на вызов функции, без классов и без проблем из статьи. Хотя даже и на вызов процедуры заменить не проблема, и самое смешное что вы далее сами это делаете. Не зря статью читали (с).

Глобальные функции можно вызывать и с ООП

Во-первых функция может быть необязательно глобальная, а создана в функции конструкторе или еще где то.

Во-вторых, я рад что вы в итоге пришли к ФП и когда нужно сделать что то гибко и просто - будете его использовать, вызывая глобальные функции. Жаль только не во всех языках это можно делать, и придется создавать нетестируемые статические классы.

User.getDisplayName() из вашего примера в статье. У него тоже нет ссылок на другие методы,

Вы видимо таки не поняли о чем речь, попробую объяснить еще проще: this - это объект класса где есть все из этого класса. А значит метод жестов завязан на тип этого класса, и его нельзя переиспользовать для данных, у которых нет всего, что есть в this. То есть если в User есть метод goFckYourself, то чтобы переисолзовать getDisplayName из User нужно будет добавить в мои данные, например Dog, этот же метод, иначе никак. Более того, единственный способ их добавить это отнаследовать Doc от User, добавив таки эти методы. Надеюсь теперь понятно, если нет - тут уже ничем не помогу.

Это можно сделать и с ООП, но вы в статье все равно это указали как проблему ООП.

В статье речь про то, что в методах класса выбора нет, а в ФП выбор есть, и даже пример есть с getDisplayName - советую почитать еще раз.

В итоге ни одно ваше утверждение не доказано, ни про "у вас это не получится", ни про "у метода нет ссылок на другие методы", ни то что можно сделать this опциональным в ООП, и даже переписать на классы JS без читинга ваш собственный, якобы заточенный на превосходство ООП пример.

Полный провал.

Но хотя бы пришли к тому, что в С++ тоже можно использовать процедуры если нужно гибко и просто. Хоть что то хорошее.

PS. Сложность кода оценивается не только строчками, а в том числе количеством символов языка (типа class со всеми вытекающими из них this, наследованием, модификаторами доступа и тп), багоемкостью (типа попытки передать метода класса как параметр, получая вместо this undefined) и прочими проблемами, перечисленными в статье. Тут у вас тоже полный провал.

Все различия связаны с синтаксисом языков JavaScript/CPP

Нет, в JS тоже есть классы и вся эта ерундистика. Я использовал более лаконичный и простой подход без лишних символов. И по сторчкам, и в целом по символам вышло меньше. Даже на специально выбранном вами примере и с точно таким же подходом без особого рефакторинг.

Методы encode и requestKeyFrame в вашей реализации тоже приколочены к this. Например если вы захотите вызывать один метод из другого, вы будете использовать this

  1. Они вообще никуда не приколочены и там нет ни одного символа this.

  2. Более того у стрелочных функций что я использую в принципе нет this.

  3. Этот пример полная копия вашего, только без классов.

Эти функции можно без проблем вынести в процедуры если возникнет такая необходимость, например:

// Аргументы только те, что используются, ничего лишнего
const encodeVp9S = (frame: Frame) => {
  // ...
}

const makeVp9SWEncoder = (): Encoder => ({
  encode: encodeVp9S
});

У вас эти методы зависят даже не от класса, а вообще от конкретного объекта.

Ограничений здесь нет, см. пред. пример.

У вас то же самое, методы encode и requestKeyFrame "требуют для работы" все методы, которые есть в объекте где они объявлены.

Опять ошибка - где они это требуют? У них даже ссылки нет друг на друга))

У вас аналогично, нельзя использовать метод без создания объекта где он содержится.

И тут ошибка - если нужно то можно - см. пред. пример.

Сможете обработать в ваших методах ситуацию, когда encoder null или undefined? Я в этом сомневаюсь.

const encodeVp9S = (frame?: Frame) => {
  if (!frame) return null
  // ...
}

encoder?.encode()
encoder?.requestFrame?.()

А в замыкании ситуакция когда оно null невозможна.

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

Какой то детский сад а не код)) Даже не буду рефакторить, выложу то же самое (78 строк).

type Encoder = {
  encode: (frame: VideoFrame) => BitStream;
  requestKeyFrame?: () => void;
};

const makeVp9SWEncoder = (): Encoder => ({
  encode: (frame) => {
    // кодируем через libvpx
  },
});

const makeVp9HWEncoder = (): Encoder => ({
  encode: (frame) => {
    // Вызываем функции ОС чтобы драйвер видеокарты что-то закодировал
  },
});

const makeSoftwareFallbackEncoder = (swEncoder: Encoder, hwEncoder: Encoder): Encoder => {
  let isFallback = false;
  
  return {
    encode: (frame) => {
      if (!isFallback) {
        const encoded = hwEncoder.encode(frame);
        if (!encoded.isError) return encoded;
        isFallback = true;
      }
      return swEncoder.encode(frame);
    },
    requestKeyFrame: () => {
      if (isFallback) swEncoder.requestKeyFrame?.();
      else hwEncoder.requestKeyFrame?.();
    }
  };
};

const makeEncoder = (codec: Codec): Encoder => {
  switch (codec) {
    case 'vp9f': return makeSoftwareFallbackEncoder(
      makeVp9SWEncoder(), makeVp9HWEncoder()
    );
    case 'vp8f': return makeSoftwareFallbackEncoder(
      makeVp9SWEncoder(), makeVp9HWEncoder()
    );
    case 'vp9s': return makeVp9SWEncoder();
    case 'vp9h': return makeVp9HWEncoder();
    case 'vp8h': return makeVp9HWEncoder();
    case 'vp8': return makeVp9SWEncoder();
    case 'vp9': return makeVp9SWEncoder();
    case 'h264': return makeVp9HWEncoder();
  }
};

const main = () => {
  // ...
  const codecs = getCodecs(); 
  const encoders = codecs.map(makeEncoder)

  while (true) {
    const event = getEvent();
    if (!event) break;
    
    switch (event.type) {
      case 'gotFrame': {
        for (const encoder of encoders) {
          encoder.encode(event.getFrame());
        }
        break;
      }
      case 'requestedKeyFrame': {
        for (const encoder of encoders) {
          encoder.requestKeyFrame?.();
        }
        break;
      }
    }
  }
};


Вы сами скинули ссылки на два куска кода, и плюс еще просите учесть свой кусок кода. У меня аналогичные куски кода.

Приведите сами ВЕСЬ код а не несколько кусков, потом поговорим.

Какой кусок кода нужно еще привести, чтобы вы смогли увидеть проблемы?)

И где ваш полный пример этого кода?)

код тоже надо переписывать

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

Правильно. А если больше, то хуже. В вашем коде проблем больше.

Я бы вам ткнул пальцем,

Ключевые слова "я бы". Я думаю можно этими двумя словами подытожить все потуги ООПшников))

ООП даст вам, по сути, только одно: объект сможет пользоваться реализацией родительского класса, при этом сам не зная, кто его родитель

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

установки is_fallback нет

В вашем последнем куске кода тоже нет.

  Как у вас сделать вот такую конструкцию?

const makeEncoder = (encoding: SoftwareEncoding | HardwareEncoding | FallbackEncoding): Encoder => {
  switch (encoding) {
    case 'vp9f': return makeSoftwareFallbackEncoder('vp9h', 'vp9s');
    case 'vp8f': return makeSoftwareFallbackEncoder('vp8h', 'vp8s');
    case 'vp9s': return makeVp9SWEncoder()
    case 'vp8s': return makeVp8SWEncoder()
    case 'vp9h': return makeVp9HWEncoder()
    case 'vp8h': return makeVp8HWEncoder()  
    // ...
  }
}

const makeSoftwareFallbackEncoder = (primary: HardwareEncoding, fallback: SoftwareEncoding): Encoder => {
  let encoder = makeEncoder(primary)
  let isFallback = false

  return () => {
    encode: (frame) => {
      if (isFallback) {
         return encoder.encode(frame) 
      }

      try {
        return encoder.encode(frame)  
      } catch {
         encoder = makeEncoder(fallback)
         isFallback = true
         return encoder.encode(frame)
      }
    },
    // ...
  }
}

вы реализовали ООП вручную

Я все проблемы ООП, перечисленные в статье, не реализовывал в своих примерах - и их там нет. А если, как вы заявляете, код очень похож, но в одном из вариантов сильно меньше проблем чем во втором - значит он лучше.

Он работает так же [плохо] как и в вашем примере.

Если это был типа "плохой" пример, то надо учиться яснее общаться. Второй ваш пример это вообще непонятно что на вашем фреймворке, в котором 1) я разбираться не имею никакого желания 2) вообще не ясно как массив редактируется и как используется, и почему у игрушки зачем то есть count().

Если вы топите за мутабельное состояние [от которого проблем часто больше, при схожей производительности], то вот простейший пример вообще без фреймворков:

type Toy = {
  count: 0
}

type State = {
  toys: Toy[],
  filteredToys: Toy[],
  filter: (x) => boolean
}

const makeToysStore = () => {
   const state: State = {
    toys: [],
    filter: (x) => x.count > 0
  }

  const setFilter = (filter: State['filter']) => {
    state.filter = filter
    state.filteredToys = state.toys.filter(filter)
  
    EventEmitter.emit('toysStoreChanged', ['filter', 'filteredToys'])
  } 

  return {
    getState: () => state,
    setFilter
  }
}

export const toysStore = makeToysStore()


Можно было бы вынести повторяющиеся паттерны в отдельный фреймворк, либо использовать готовые решения, но суть в том, что это все прекрасно реализуется в ФП.

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

Так статистику поищите и не сомневайтесь больше.

Когда Вы уже осознаете, что язык/фреймворк это инструмент

Как только хоть один человек скинет код, на котором будет видно, как забивать гвозди связанными скотчем костылями (ООП) лучше, чем молотком (ФП).

Я привел аж три способа реализации полиморфизма (union type, обобщение и интерфейс), и даже указал в каких случаях их лучше использовать, и даже реализовал где то здесь в комментариях код на обобщениях, который якобы "нельзя реализовать на ФП".

Вот фраза из статьи, с дальнейшими примерами кода:

Далее в примере для этого будут использоваться union type, обобщение и интерфейс:

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

Вот пример мобильного приложения https://github.com/mattermost/mattermost-mobile. Там же есть и десктоп, и веб.

Вы не специалист, наконец то признали, тогда есть чему поучиться.

Во-первых ООП в моем видении, описаном в статье, в приведенном коде отсутствует. Это просто псевдо-декларативное описание модели и поведения, на основе которой генерируется ООП код, а мог бы генерироваться и ФП с тем же "успехом".

Почему псевдо - потому что если слишком много логики запихнуть в декларативность, то как работает код поймет только его автор [и Майкл Джексон]. Так же он будет обладать большим количеством ограничений, на который наткнешься уже по факту, и чтоб от них избавиться придется все переписывать. И чтобы реализовать даже простейшую задачу, придется очень долго и нудно читать документацию этого фреймворка. Поэтому ваш фреймворк обречен, к сожалению.

Другими словами, вы не выполнили главное требование инженера - чем проще, тем лучше.

Вот как выглядит хороший, простой код (псевдокод) на функциях:

// Модель отделена от логики
type User = {
  id: number
  name?: string
  description?: string
  created_at?: number
  avatarUrl?: string
  hobbies?: string
}

// Хранение состояния отделено от UI и слоя доступа к данным.
// Здесь мог бы быть любой стор - мутабельный, иммутабельный, на прокси или евент емиттерах и тп.

type EntitiesState = {
  users: User[]
  userConfigs: UserConfig[]
}

const entitiesStore = create<EntitiesState>({
  users: [],
  userConfigs: [],
})

// Бизнес логика.

const changeUserConfig: (userConfig: Partial<UserConfig> & Pick<UserConfig, "id">) => {
  // Меняем значение в entitiesStore.
}

// UI - любая библиотека на усмотрение.

const UserPage = ({id}: {id: string}) => {
  const user = useEntitiesStore((state) => selectUser(state, id))

  if (!user) return <Loader />
    
  return (
    <Html>
      <Title value={user.name} />
      <Description value={user.description} />
      // ...
    </Html>
  )
}

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

Дождались наконец.

  1. При чем тут процедурная?

  2. Примеров все еще нет.

1
23 ...

Информация

В рейтинге
Не участвует
Откуда
Санкт-Петербург, Санкт-Петербург и область, Россия
Зарегистрирован
Активность

Специализация

Фронтенд разработчик, Разработчик мобильных приложений
Ведущий