Как я разрабатываю sub класс с функциями, не доступными в базовом классе?

Две вещи:

  1. Вы не можете сделать это только с map.
  2. Ваш return находится не в том месте, что является причиной синтаксической ошибки.

Чтобы сделать это за один проход, самое простое - использовать forEach и push (поскольку скрипт Apps не поддерживает for-of):

var replies = [];
conversation.forEach(function(message, index) {
    if (message.indexOf("R:") !== -1) {
        replies.push(index);
    }
});

Вы можете сделать это в два прохода с map и filter:

var replies = conversation
    .map(function(message, index) {
        return message.indexOf("R:") !== -1 ? index : -1;
    })
    .filter(function(index) {
        return index !== -1;
    });
5
задан strager 17 April 2009 в 02:43
поделиться

8 ответов

Я делал это в похожих ситуациях.

Вариант А)

Если специализированные операции являются частью из той же последовательности, что и базовая операция (например, ConvertibleVehicle необходимо свернуть, прежде чем она сможет двигаться), затем просто поместите специализированную операцию в базовую операцию.

class Vehicle { 
     public abstract void drive();
}

class ConvertibleVehicle { 
     public void drive() { 
         this.foldRoof();
         .... // drive 
     }
     private void foldRoof() { 
         ....
     }
 }

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

 for( Vehicle v : vehicleFleet ) { 
      v.drive();
 }

Специализированный метод не предоставляется в общедоступном интерфейсе объекта, но вызывается при необходимости.

Вариант B)

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

class Vehicle { 
    public void drive() { 
        ....
    }
}
class ConvertibleVehicle extends Vehicle { 
         // specialized version may override base operation or may not.
        public void drive() { 
          ... 
         }

         public void foldRoof() { // specialized operation 
             ...
         }
 }

Почти так же, как и в предыдущем примере, только в этом случае foldRoof также является публичным.

Разница в том, что мне нужен специализированный клиент:

// Client ( base handler ) 
public class FleetHandler { 
     public void handle( Vehicle [] fleet ) { 
           for( Vehicle v : fleet ) {  
               v.drive();
            }
     }
}

// Specialized client ( sophisticate handler that is )  
 public class RoofAwareFleetHandler extends FleetHandler { 
      public void handle( Vehicle [] fleet ) { 
           for( Vehicle v : fleet ) { 
              // there are two options.
              // either all vehicles are ConvertibleVehicles (risky) then
              ((ConvertibleVehicles)v).foldRoof();
              v.drive();

              // Or.. only some of them are ( safer ) .
              if( v instenceOf ConvertibleVehicle ) { 
                  ((ConvertibleVehicles)v).foldRoof();
              } 
              v.drive();
            }
       }
  }

Это instaceof Выглядит немного уродливо, но это может быть подчеркнуто современным vm.

Дело в том, что только специализированный клиент знает и может вызывать специализированные методы. То есть только RoofAwareFleetHandler может вызвать foldRoof () для ** ConvertibleVehicle **

Окончательный код не изменяется ...

 public class Main { 
     public static void main( String [] args ) { 
         FleetHandler fleetHandler = .....
         Vehicles [] fleet =  ....

          fleetHandler.handle( fleet );
      }
 }

Конечно,

3
ответ дан 18 December 2019 в 13:19
поделиться

Любые объекты, которые используют Vehicle, не должны знать о ConvertibleVehicle и его специализированных методах. В надлежащем слабо связанном объектно-ориентированном дизайне, водитель будет знать только об интерфейсе автомобиля. Драйвер может вызывать startEngine () для Vehicle, но это зависит от подклассов Vehicle, чтобы переопределить startEngine () для обработки различных реализаций, таких как поворот ключа или нажатие кнопки.

Рассмотрите возможность рассмотрения следующих двух ссылок, которые должны помочь объяснить эта концепция: http://en.wikipedia.org/wiki/Liskov_substitution_principle http://en.wikipedia.org/wiki/Open/closed_principle

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

5
ответ дан 18 December 2019 в 13:19
поделиться

Это хороший вопрос. Это означает, что у вас есть (или вы ожидаете иметь) код, который запрашивает у Vehicle (например) foldRoof (). И это проблема, потому что большинство транспортных средств не должны складывать свои крыши. Только код, который знает, что имеет дело с ConvertibleVehicle, должен вызывать этот метод, это означает, что это метод, который должен быть только в классе ConvertibleVehicle. Так будет лучше; как только вы попытаетесь вызвать Vehicle.foldRoof (), ваш редактор сообщит вам, что это невозможно сделать. Это означает, что вам нужно либо расположить код так, чтобы вы знали, что имеете дело с ConvertibleVehicle, либо отменить вызов foldRoof ().

3
ответ дан 18 December 2019 в 13:19
поделиться

Я думаю, что большинство людей не понимают сути вопроса Дельты. Мне кажется, что он / она не спрашивает о наследовании. Он / она спрашивает о подклассах, реализующих функциональность, которая не является естественной для базового класса, и о возможной путанице. Т.е. подталкивание определенных методов / функциональных возможностей вверх по цепочке иерархии или требование, чтобы подклассы реализовывали контракт для функциональных возможностей, которые не являются естественным соответствием.

Существует также вопрос, насколько ценно иметь возможность обрабатывать базу класс как подкласс в каждом случае (чтобы избежать приведения и использовать их взаимозаменяемо). * edit - это называется принцип подстановки Лискова (спасибо, что напомнил, Кайл).

1
ответ дан 18 December 2019 в 13:19
поделиться

Это то, что делает подклассы: добавляет функциональность, отсутствующую в базовом классе.

class MyVehicle : public Vehicle {

public: 
  void MyNewFunction()
...

Есть два (на самом деле просто) разные виды наследования: публичное и частное, отражающие отношения Is-A и Has-A соответственно. С помощью публичного наследования вы напрямую добавляете материал в класс. Если у меня есть класс Animal с методами Eat () и Walk (), я могу создать подкласс Cat с методом Purr (). Затем у Cat есть открытые методы Eat, Walk и Purr.

Однако, в случае стека, основанного на LinkedList, я могу сказать, что Stack HAS-A LinkedList внутренне. Таким образом, я не раскрываю какие-либо функции базового класса публично, я сохраняю их как частные и должен явно предлагать все, что я выберу общедоступными. Список может иметь метод Insert (), но для стека, Я ограничиваю реализацию и переэкспонирую ее как Push (). Предыдущие открытые методы не доступны.

В C ++ это определяется модификатором доступа, указанным перед базовым классом. Выше я использую публичное наследование. Здесь я использую частное наследование:

class MyVehicle : private Engine {

Это отражает то, что MyVehicle HAS-An Engine.

В конечном счете, подклассы отбирают все доступное в базовом классе и добавляют к нему новые вещи. [Тысяча двести тридцать шесть] EDIT: С этой новой информацией кажется, что вы действительно ищете интерфейсы, как указано в предыдущем (отклоненном) комментарии. Это одна из больших проблем с наследованием - гранулярность. Одна из главных претензий C ++ - это реализация множественного наследования (вариант для этого). Можете ли вы указать, какой язык вы используете, чтобы мы могли правильно посоветовать?

1
ответ дан 18 December 2019 в 13:19
поделиться

To add on to Kyle W. Cartmell's excellent answer, and to perhaps simplify Oscar Reyes's answer a tad...

You might want to consider having the base class define a method called prepareToDrive() where inherited classes could put any setup tasks that need to be done before starting up. Calling drive() would be the way to start everything up from the user's perspective, so we would need to refactor drive into a "setup" phase and a "go" phase.

public class Vehicle {
    protected void prepareToDrive() {
        // no-op in the base class
    }

    protected abstract void go();

    public final void drive() {
        prepareToDrive();
        go();
    }
}

Now, subclasses must implement the protected method go() (really bad method name, but you get the idea), which is where they do their class-specific handling of driving.

Now, your inherited class could look like this:

public class ConvertableVehicle extends Vehicle {

    // override setup method
    protected void prepareToDrive() {
        foldRoof();
    }

    protected void go() {
        // however this works
    }

    protected void foldRoof() {
        // ... whatever ...
    }
}

This structure would also help when you run into class TractorTrailerRig that needs to make sure the trailer is loaded and correctly attached before it can drive.

1
ответ дан 18 December 2019 в 13:19
поделиться

Как пользователь автомобиля узнает, что это автомобиль с откидным верхом? Либо им нужно динамическое приведение, чтобы убедиться, что оно правильное, либо вы предоставили метод в Vehicle для получения реального типа объектов.

В первом случае у пользователя уже есть ConvertibleVehicle как часть динамического приведения. Они могут просто использовать новый указатель / ссылку для доступа к методам ConvertiblVehicle

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

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

Все это говорит. Мне нужны все методы производных классов из базового класса. Я мог бы привести к производному классу, но он был вовлечен в каркас и потребовал бы гораздо больше усилий. Старая поговорка «все проблемы можно решить с помощью еще одного слоя косвенности» - вот как я решил это. Я вызвал виртуальный метод в базовом классе с именем функции, которую я хотел вызвать. «Имя» может быть строкой или целым числом в зависимости от ваших потребностей. Это медленнее, но вам нужно будет делать это редко, если ваша иерархия классов достаточно выразительна.

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

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

0
ответ дан 18 December 2019 в 13:19
поделиться

Наличие ConvertibleVehicle подкласса Vehicle и добавление собственных методов, как вы описали, совершенно нормально. Эта часть дизайна в порядке. Проблемы у вас возникают с флотом. ConvertibleFleet не должен быть подклассом VehicleFleet. Пример покажет вам, почему. Допустим, VehicleFleet выглядит так:

public class VehicleFleet {
    // other stuff...

    public void add(Vehicle vehicle) {
        // adds to collection...
    }
}

Это совершенно нормально и разумно, вы можете добавить любой Vehicle или его подкласс в VehicleFleet. Теперь, допустим, у нас есть еще один вид транспортного средства:

public class TruckVehicle extends Vehicle {
    // truck-specific methods...
}

Мы также можем добавить его в VehicleFleet, поскольку это транспортное средство. Проблема заключается в следующем: если ConvertibleFleet является подклассом VehicleFleet, это означает, что мы также можем добавить грузовики в ConvertibleFleet. Это неверно. ConvertibleFleet является не правильным подклассом, поскольку операция, допустимая для его родителя (добавление грузовика), не допустима для дочернего класса.

Типичным решением является использование параметра типа:

public class VehicleFleet<T extends Vehicle> {
    void add(T vehicle) {
        // add to collection...
    }
}

Это позволит вам определить парки, специфичные для определенных типов транспортных средств. Обратите внимание, что это также означает отсутствие "базового" класса VehicleFleet, который можно передавать функциям, не заботящимся о том, какой тип транспортного средства имеет парк. Это можно исправить с помощью другого уровня базового класса (или интерфейса):

public interface VehicleFleetBase {
    Vehicle find(String name);
    // note that 'add' methods or methods that pass in vehicles to the fleet
    // should *not* be in here
}

public class VehicleFleet<T extends Vehicle> {
    void add(T vehicle) {
        // add to collection...
    }

    Vehicle find(String name) {
        // look up vehicle...
    }
}

Для методов, которые извлекают транспортные средства из флотов и не заботятся об их типе, вы можете передавать VehicleFleetBase. Методы, которым нужно вставлять транспортные средства, используют VehicleFleet, который безопасно сильно типизирован.

0
ответ дан 18 December 2019 в 13:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: