LINUX.ORG.RU

Динамическое локальное переопределение методов класса


0

1

Здравствуйте. Подскажите в каких ЯП присутствует возможность динамически локально переопределять методы класса?

Что-то вроде такого(слово public не писал просто, чтобы не мешалось, а так методы публичные):

class A {
    int id() {
        return 0;
    }
}

class App {

    static A make() {
        return new A();
    }
    
    static void print(A a) {
        System.out.println(a.id());
    }
    
    class B overrides A { // класс B равнозначен классу A,
        int id() {        // но переопределяет метод id()
            return 1;
        }
    }
    
    static void forObject(A a) {

        print(a); => 0
        print(a as B); // => 1
        print(a as A {
            int id() {
                return 2;
            }
        }); // => 2
        print(a); => 0
    }
    
    static void forClass(A a) {

        print(a); => 0
        override A as B;
        print(a); // => 1
        override A {
            int id() {
                return 2;
            }
        }
        print(a); // => 2
    }
    
    static void main(String[] args) {
    
        A a = make();
        print(a); => 0
        forObject(a);
        forClass(a);
        print(a); => 0
    }
}

Или хотя бы часть из того, что есть в примере.

P.S. Классово/методовое ООП не обязательно, процедуры или функции тоже подойдут.

P.P.S. Кроме CL =)

★★★★★

Да в любом динамически типизированном, наверное.

tailgunner ★★★★★
()
class A(object):
    def id(self):
        return 0


a1 = A()
a2 = A()

print a1.id(), a2.id()

A.id = lambda self: 1 # For class
print a1.id(), a2.id()

a1.id = lambda: 2 # For instance, bounded
print a1.id(), a2.id()

a2.id = (lambda self:3).__get__(a2) # For instance, unbounded
print a1.id(), a2.id()
baverman ★★★
()

Пример переопределения метода для экземпляра класса в Ruby:

str1 = "Йа строка"
str2 = "И йа строка"

class <<str2
  def length
    'хрен тебе, а не размер'
  end
end

[str1, str2].each {|s| puts "Строка \"#{s}\" имеет размер: #{s.length}" }
geekless ★★
()

Не знаю, как на практике, но имхо это очень опасные и неочевидные конструкции. Соизмеримо с бездумным использованием плюсовых blahblahblah_cast<...>(...)

xydo ★★
()
Ответ на: комментарий от xydo

PS: я имею в виду вот это:

        print(a); => 0
        override A as B;
        print(a); // => 1

а не само переопределение методов, конечно.

(Оффтопнул?)

xydo ★★
()
Ответ на: комментарий от baverman
class A(object):
    def id(self):
        return 0


a1 = A()
a2 = A()

print a1.id(), a2.id() # => 0 0 — ok

def foo():
    A.id = lambda self: 1 # For class
    print a1.id(), a2.id()

foo() # => 1 1 — ok

print a1.id(), a2.id() # => 1 1 — не катит, должно быть 0 0

def bar(a):
    a.id = lambda: 2
    print a.id()

bar(a1) # => 2 — ok

print a1.id() # => 2 — не катит, должно быть 1
korvin_ ★★★★★
() автор топика
Ответ на: комментарий от xydo

Все равно не очень понятно в чем именно опасность. Хотя... Да, я понимаю о чем ты, есть возможные неприятности. Впрочем как и в любом другом коде, не более.

korvin_ ★★★★★
() автор топика
Ответ на: комментарий от dave

Кстати динамические переменные тут не совсем подходят:

(defvar x 0)

(defun foo ()
  (let ((x 1))
    (format t "~a~%" x)
    x))

(let ((y (foo)))
  (format t "~a~%" y))

; 1 — (x) ok
; 1 — (y) не ok, должно быть 0
Параметры в Racket тоже почти подходят:
(define x (make-parameter 0))

(define (foo)
  (parameterize ((x 1))
    (printf "~a\n" (x))
    x))

(let ((y (foo)))
  (printf "~a\n" (y)))

; 1 — (x) ok
; 0 — (y) ok

Но и различия видны и вполне понятно, чем CL код отличается от Racket, и почему результат у CL такой, а не другой.

korvin_ ★★★★★
() автор топика
Ответ на: комментарий от archimag

JavaScript?

Я думал о ней, когда писал P.P.S =) Но так как знаком с ней совсем чуть-чуть, то не мог знать наверняка. Не составит ли тебе труда показать пример, аналогичный тому, что я описал в стартовом сообщении?

korvin_ ★★★★★
() автор топика

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

yoghurt ★★★★★
()
Ответ на: комментарий от korvin_

Вон оно чо, Михалыч. Тогда так:

from contextlib import contextmanager

@contextmanager
def override(obj, **kwargs):
    oldvalues = dict((attr, getattr(obj, attr)) for attr in kwargs)

    for attr, value in kwargs.iteritems():
        setattr(obj, attr, value)

    try:
        yield
    finally:
        for attr, value in oldvalues.iteritems():
            setattr(obj, attr, value)


class A(object):
    def id(self):
        return 0


a1 = A()
a2 = A()

with override(A, id=lambda self: 1):
    assert (a1.id(), a2.id()) == (1, 1)

assert (a1.id(), a2.id()) == (0, 0)


with override(a1, id=lambda: 2):
    assert a1.id() == 2

assert a1.id() == 0
baverman ★★★
()

extern void f1_1(int);
extern void f1_2(int);
extern void f1_3(int);
extern int f2_1();
extern int f2_2();
extern int f2_3();

typedef void (*f1_t)(int);
typedef int (*f2_t)();

class A {
public:
  f1_t M1;
  f2_t M2;

  A() { M1 = &f1_2;  M2  = &f2_3;}
  Hrenak() { M1 = &f1_1; M2 = &f2_1; }
}

A a;

a->M1(23);
a->Hrenak();
a->M1(23);

Но вообще это тупак.

Jetty ★★★★★
()
Ответ на: комментарий от Jetty

Но вообще это тупак.

Это вообще не то.

korvin_ ★★★★★
() автор топика
Ответ на: комментарий от korvin_

В многопоточном приложении проблем не будет?

Конечно будут. Хотя, сюда можно вкорячить thread locals, а в обертках для переопределенных методов — соответствующий диспатчинг. Это еще строк 30-50 кода.

Но, вообще говоря, эта конструкция уже сейчас вызывают головную боль своей неявностью, пистонщики такое не любят.

baverman ★★★
()

А где тут, собственно, динамическое переопределение? Все вполне статично.

anonymous
()

(den73)

В Дельфи (можно написать) changeClass - несколько строк на ассемблере. Позволяет динамически заменить класс экземпляра на другой. Имеет смысл заменять на потомка, у к-рого данный метод перекрыт. Также есть методика открытия private/protected полей класса, что ломает систему защиты и позволяет обращаться к любым полям класса из внешних процедур. Правда, для этого нужен исходный текст декларации класса, к-рый в Delphi не всегда доступен (или это только я не умею его доставать из скомпилированных файлов, хотя есть вроде декомпилятор).

Если класс растёт от TComponent, к экземпляру почти всегда можно присоединить произвольные данные, добавив их в список подкомпонент экземпляра. Это - не совсем про то, но позволяет моделировать eq хеш-таблицы (я этим интенсивно пользуюсь), а значит, позволяет достаточно общим способом реализовать методы, определённые на конкретном экземпляре.

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

Я также пробовал добавлять произвольные данные к объекту путём перешибания менеджера памтяи, вроде это заработало, но было стрёмно пользоваться и я не стал.

По-моему, в Objective-C тоже можно что-то делать. Не знаю, как насчёт перекрытия и как на уровне экземпляра, но вроде можно добавлять методы к классу из других модулей, зачастую этого достаточно.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.